[#1157] Add SQL / JDBC tracing capabilities in addition to logging - Added support for batch queries
This commit is contained in:
parent
bff646e7e8
commit
3921e0f0ca
@ -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.
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
Loading…
Reference in New Issue
Block a user