[#1157] Add SQL / JDBC tracing capabilities in addition to logging - Added support for batch queries

This commit is contained in:
Lukas Eder 2012-02-23 21:01:50 +00:00
parent bff646e7e8
commit 3921e0f0ca
6 changed files with 201 additions and 113 deletions

View File

@ -66,11 +66,31 @@ public interface ExecuteContext extends Configuration {
/**
* The jOOQ {@link Query} that is being executed or <code>null</code> if the
* query is unknown or if there was no jOOQ <code>Query</code>
* <p>
* If {@link #batchQueries()} returns several <code>Query</code> objects,
* this will always return the currently rendered / prepared
* <code>Query</code>, or <code>null</code>, if no <code>Query</code> is
* currently being rendered / prepared
*
* @see #routine()
* @see #batchQueries()
*/
Query query();
/**
* The jOOQ {@link Query} objects that are being executed in batch mode, or
* <code>null</code> if the query is unknown or if there was no jOOQ
* <code>Query</code>
* <p>
* If a single <code>Query</code> is executed in non-batch mode, this will
* return an array of length <code>1</code>, containing that
* <code>Query</code>
*
* @see #query()
* @see #routine()
*/
Query[] batchQueries();
/**
* The jOOQ {@link Routine} that is being executed or <code>null</code> if
* the query is unknown or if there was no jOOQ <code>Routine</code>
@ -94,6 +114,19 @@ public interface ExecuteContext extends Configuration {
*/
void sql(String sql);
/**
* The generated SQL statements that are being executed in batch mode, or
* <code>null</code> if the query is unknown or if there was no SQL statement
* <p>
* If a single <code>Query</code> is executed in non-batch mode, this will
* return an array of length <code>1</code>, containing that
* <code>Query</code>
*
* @see #query()
* @see #routine()
*/
String[] batchSQL();
/**
* The {@link PreparedStatement} that is being executed or <code>null</code>
* if the statement is unknown or if there was no statement.

View File

@ -73,119 +73,152 @@ import org.jooq.tools.StopWatchListener;
* <th>Use case [2]</th>
* <th>Use case [3]</th>
* <th>Use case [4]</th>
* <th>Use case [5]</th>
* <th>Use case [6]</th>
* </tr>
* <tr>
* <td> {@link #start(ExecuteContext)}</code></td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #renderStart(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, Nx (for every query)</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #renderEnd(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, Nx (for every query)</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #prepareStart(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, Nx (for every query)</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #prepareEnd(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, Nx (for every query)</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #bindStart(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>No</td>
* <td>Yes</td>
* <td>Yes, Nx (for every value set)</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #bindEnd(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>No</td>
* <td>Yes</td>
* </tr>
* <td>Yes, Nx (for every value set)</td>
* <td>Yes, 1x</td>
* <td>Yes, 1
* <tr>
* <td> {@link #executeStart(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #executeEnd(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>Yes</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* </tr>
* <tr>
* <td> {@link #fetchStart(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x (Nx for {@link ResultQuery#fetchMany()}</td>
* <td>Yes, 1x (Nx for {@link ResultQuery#fetchMany()}</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>No</td>
* <td>No</td>
* </tr>
* <tr>
* <td> {@link #resultStart(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x (Nx for {@link Cursor#fetch(int)}</td>
* <td>Yes, 1x (Nx for {@link Cursor#fetch(int)}</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>No</td>
* <td>No</td>
* </tr>
* <tr>
* <td> {@link #recordStart(ExecuteContext)}<br/>
* </td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, Nx</td>
* <td>Yes, Nx</td>
* <td>Yes, Nx</td>
* <td>No</td>
* <td>No</td>
* <td>No</td>
* </tr>
* <tr>
* <td> {@link #recordEnd(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, Nx</td>
* <td>Yes, Nx</td>
* <td>Yes, Nx</td>
* <td>No</td>
* <td>No</td>
* <td>No</td>
* </tr>
* <tr>
* <td> {@link #resultEnd(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x (Nx for {@link Cursor#fetch(int)}</td>
* <td>Yes, 1x (Nx for {@link Cursor#fetch(int)}</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>No</td>
* <td>No</td>
* </tr>
* <tr>
* <td> {@link #fetchEnd(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes, 1x (Nx for {@link ResultQuery#fetchMany()}</td>
* <td>Yes, 1x (Nx for {@link ResultQuery#fetchMany()}</td>
* <td>Yes, 1x</td>
* <td>No</td>
* <td>No</td>
* <td>No</td>
* </tr>
* <tr>
* <td> {@link #end(ExecuteContext)}</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>Yes</td>
* <td>No</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* <td>Yes, 1x</td>
* </tr>
* </table>
* <br/>
@ -197,6 +230,8 @@ import org.jooq.tools.StopWatchListener;
* {@link StatementType#STATIC_STATEMENT}</li>
* <li>Used with {@link Factory#fetch(ResultSet)} or with
* {@link InsertResultStep#fetch()}</li>
* <li>Used with {@link Factory#batch(Query)}</li>
* <li>Used with {@link Factory#batch(Query[])}</li>
* <li>Used with a {@link Routine} standalone call</li>
* </ol>
* <p>

View File

@ -37,23 +37,17 @@ package org.jooq.impl;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.jooq.Batch;
import org.jooq.ExecuteContext;
import org.jooq.ExecuteListener;
import org.jooq.Query;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StopWatch;
/**
* @author Lukas Eder
*/
class BatchMultiple implements Batch {
/**
* Generated UID
*/
private static final JooqLogger log = JooqLogger.getLogger(BatchMultiple.class);
private final Factory create;
private final Query[] queries;
@ -64,35 +58,40 @@ class BatchMultiple implements Batch {
@Override
public final int[] execute() {
StopWatch watch = new StopWatch();
Connection connection = create.getConnection();
Statement statement = null;
String sql = null;
ExecuteContext ctx = new DefaultExecuteContext(create, queries);
ExecuteListener listener = new ExecuteListeners(ctx);
try {
statement = connection.createStatement();
ctx.statement(new PreparedStatementProxy(connection));
for (Query query : queries) {
sql = create.renderInlined(query);
watch.splitTrace("SQL rendered");
if (log.isDebugEnabled())
log.debug("Adding batch", sql);
statement.addBatch(sql);
String[] batchSQL = ctx.batchSQL();
for (int i = 0; i < queries.length; i++) {
listener.renderStart(ctx);
batchSQL[i] = create.renderInlined(queries[i]);
listener.renderEnd(ctx);
}
int[] result = statement.executeBatch();
watch.splitTrace("Statement executed");
for (String sql : batchSQL) {
ctx.sql(sql);
listener.prepareStart(ctx);
ctx.statement().addBatch(sql);
listener.prepareEnd(ctx);
ctx.sql(null);
}
listener.executeStart(ctx);
int[] result = ctx.statement().executeBatch();
listener.executeEnd(ctx);
return result;
}
catch (SQLException e) {
throw Util.translate("BatchMultiple.execute", sql, e);
throw Util.translate("BatchMultiple.execute", ctx.sql(), e);
}
finally {
Util.safeClose(statement);
watch.splitDebug("Statement executed");
Util.safeClose(listener, ctx);
}
}
}

View File

@ -36,26 +36,20 @@
package org.jooq.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.jooq.BatchBindStep;
import org.jooq.ExecuteContext;
import org.jooq.ExecuteListener;
import org.jooq.Query;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StopWatch;
/**
* @author Lukas Eder
*/
class BatchSingle implements BatchBindStep {
/**
* Generated UID
*/
private static final JooqLogger log = JooqLogger.getLogger(BatchSingle.class);
private final Factory create;
private final Query query;
private final List<Object[]> allBindValues;
@ -74,41 +68,39 @@ class BatchSingle implements BatchBindStep {
@Override
public final int[] execute() {
StopWatch watch = new StopWatch();
Connection connection = create.getConnection();
PreparedStatement statement = null;
String sql = null;
ExecuteContext ctx = new DefaultExecuteContext(create, query);
ExecuteListener listener = new ExecuteListeners(ctx);
try {
listener.renderStart(ctx);
ctx.sql(create.render(query));
listener.renderEnd(ctx);
sql = create.render(query);
watch.splitTrace("SQL rendered");
if (log.isDebugEnabled())
log.debug("Executing query", create.renderInlined(query));
if (log.isTraceEnabled())
log.trace("Preparing statement", sql);
statement = connection.prepareStatement(sql);
watch.splitTrace("Statement prepared");
listener.prepareStart(ctx);
ctx.statement(connection.prepareStatement(ctx.sql()));
listener.prepareEnd(ctx);
for (Object[] bindValues : allBindValues) {
new DefaultBindContext(create, statement).bindValues(bindValues);
statement.addBatch();
listener.bindStart(ctx);
new DefaultBindContext(create, ctx.statement()).bindValues(bindValues);
listener.bindEnd(ctx);
ctx.statement().addBatch();
}
watch.splitTrace("Variables bound");
int[] result = statement.executeBatch();
watch.splitTrace("Statement executed");
listener.executeStart(ctx);
int[] result = ctx.statement().executeBatch();
listener.executeEnd(ctx);
return result;
}
catch (SQLException e) {
throw Util.translate("BatchSingle.execute", sql, e);
throw Util.translate("BatchSingle.execute", ctx.sql(), e);
}
finally {
Util.safeClose(statement);
watch.splitDebug("Statement executed");
Util.safeClose(listener, ctx);
}
}
}

View File

@ -68,6 +68,9 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont
private final Routine<?> routine;
private String sql;
private final Query[] batchQueries;
private final String[] batchSQL;
// Transient attributes
private transient PreparedStatement statement;
private transient ResultSet resultSet;
@ -75,22 +78,28 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont
private transient Result<?> result;
DefaultExecuteContext(Configuration configuration) {
this(configuration, null, null);
this(configuration, null, null, null);
}
DefaultExecuteContext(Configuration configuration, Query[] batchQueries) {
this(configuration, null, batchQueries, null);
}
DefaultExecuteContext(Configuration configuration, Query query) {
this(configuration, query, null);
this(configuration, query, new Query[] { query }, null);
}
DefaultExecuteContext(Configuration configuration, Routine<?> routine) {
this(configuration, null, routine);
this(configuration, null, null, routine);
}
private DefaultExecuteContext(Configuration configuration, Query query, Routine<?> routine) {
private DefaultExecuteContext(Configuration configuration, Query query, Query[] batchQueries, Routine<?> routine) {
super(configuration);
this.query = query;
this.batchQueries = batchQueries;
this.routine = routine;
this.batchSQL = (batchQueries == null ? null : new String[batchQueries.length]);
}
@Override
@ -122,6 +131,11 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont
return query;
}
@Override
public final Query[] batchQueries() {
return batchQueries;
}
@Override
public final Routine<?> routine() {
return routine;
@ -137,6 +151,11 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont
return sql;
}
@Override
public final String[] batchSQL() {
return batchSQL;
}
@Override
public final void statement(PreparedStatement s) {
this.statement = s;

View File

@ -87,7 +87,11 @@ class PreparedStatementProxy implements PreparedStatement {
}
private PreparedStatementProxy(Connection connection, String sql, MethodType type) throws SQLException {
this(connection, sql, type, connection.createStatement());
this(connection, sql, type, connection.createStatement());
}
PreparedStatementProxy(Connection connection) throws SQLException {
this(connection, null, MethodType.BATCH);
}
PreparedStatementProxy(Connection connection, String sql) throws SQLException {
@ -161,7 +165,13 @@ class PreparedStatementProxy implements PreparedStatement {
* Corresponds to
* {@link Connection#prepareStatement(String, String[])
*/
SQL_CN
SQL_CN,
/**
* Corresponds to {@link Connection#createStatement()} and
* {@link Statement#executeBatch()}
*/
BATCH
}
// ------------------------------------------------------------------------
@ -364,7 +374,7 @@ class PreparedStatementProxy implements PreparedStatement {
}
// ------------------------------------------------------------------------
// XXX: Unsupported batch methods
// XXX: Supported and unsupported batch methods
// ------------------------------------------------------------------------
@Override
@ -374,17 +384,17 @@ class PreparedStatementProxy implements PreparedStatement {
@Override
public final void clearBatch() throws SQLException {
throw new UnsupportedOperationException("Cannot batch execute statements on PreparedStatementProxy");
delegate.clearBatch();
}
@Override
public final int[] executeBatch() throws SQLException {
throw new UnsupportedOperationException("Cannot batch execute statements on PreparedStatementProxy");
return delegate.executeBatch();
}
@Override
public final void addBatch(String query) throws SQLException {
throw new UnsupportedOperationException("Cannot batch execute statements on PreparedStatementProxy");
delegate.addBatch(query);
}
// ------------------------------------------------------------------------