将 JOOQ 查询与 JDBC 交易混合
Mix JOOQ query with JDBC transaction
我有一个用例,我想将 jdbc 事务与 jooq 上下文混合。
JDBC 代码如下所示:
public void inTransaction(InTransaction lambda) {
DataSource ds = dataSource.get();
try (Connection connection = ds.getConnection()) {
try {
logger.info("set autocommit to false");
connection.setAutoCommit(false);
try (Statement statement = connection.createStatement()) {
lambda.execute(statement);
logger.info("commiting transaction");
connection.commit();
}
} catch (RuntimeException e) {
logger.info("rolling back transaction");
connection.rollback();
throw e;
} finally {
logger.info("set autocommit to true");
connection.setAutoCommit(true);
}
} catch (SQLException e) {
throw new TilerException(e);
}
}
@FunctionalInterface
public interface InTransaction {
void execute(Statement statement) throws SQLException;
}
我希望 lambda
参数能够与 jdbc 和 jooq 一起使用。
For jdbc 使用语句非常简单。例如像这样的 tutorail:
inTransaction(stmt -> {
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
});
为了在同一个事务上执行 jooq 查询,我必须获得一个上下文。我找到了一个 api 从 datasource/connection.
获取 DSLContext
我不清楚的是 if/how 从语句中创建一个 jooq DSLContext
?
如果你想从 jOOQ 获取查询字符串,你可以调用
String sqlString = query.getSQL()
然后在您的语句中使用此字符串:
stmt.executeUpdate(sqlString);
您描述的问题的解决方案
你可以用 jOOQ 的交易完成所有这些 API:
// Create this ad-hoc, or inject it, or whatever
DSLContext ctx = DSL.using(dataSource, dialect);
然后:
public void inJDBCTransaction(InJDBCTransaction lambda) {
ctx.transaction(config -> {
config.dsl().connection(connection -> {
try (Statement statement = connection.createStatement()) {
lambda.execute(statement);
}
});
});
}
public void inJOOQTransaction(InJOOQTransaction lambda) {
ctx.transaction(config -> lambda.execute(config.dsl()));
}
@FunctionalInterface
public interface InJDBCTransaction {
void execute(Statement statement) throws SQLException;
}
@FunctionalInterface
public interface InJOOQTransaction {
void execute(DSLContext ctx);
}
您的最终代码:
inJDBCTransaction(stmt -> {
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
});
inJOOQTransaction(ctx -> {
ctx.insertInto(EMPLOYEES).values(106, 20, "Rita", "Tez").execute();
ctx.insertInto(EMPLOYEES).values(107, 22, "Sita", "Singh").execute();
});
我不太相信需要对 jOOQ 和 JDBC 进行这种抽象。 jOOQ 从不向你隐藏 JDBC。使用 DSLContext.connection()
方法时,您始终可以访问 JDBC API,如上所示。所以,如上图:
- jOOQ 事务 API 完全符合您的计划。在事务上下文中包装一个 lambda,如果成功则提交,如果失败则回滚(您的版本回滚不起作用,因为它捕获了错误的异常)。
- 如果需要“JDBC 逃生口”,jOOQ 可以提供
旁注
在许多 RDBMS 中,您不希望 运行 查询静态 JDBC Statement
. You'll want to use PreparedStatement
,因为:
- 您将从执行计划缓存中获益(并减少对缓存的争用)
- 您将避免语法错误(如果您的实际查询是动态的)
- 您将避免 SQL 注入问题
我有一个用例,我想将 jdbc 事务与 jooq 上下文混合。
JDBC 代码如下所示:
public void inTransaction(InTransaction lambda) {
DataSource ds = dataSource.get();
try (Connection connection = ds.getConnection()) {
try {
logger.info("set autocommit to false");
connection.setAutoCommit(false);
try (Statement statement = connection.createStatement()) {
lambda.execute(statement);
logger.info("commiting transaction");
connection.commit();
}
} catch (RuntimeException e) {
logger.info("rolling back transaction");
connection.rollback();
throw e;
} finally {
logger.info("set autocommit to true");
connection.setAutoCommit(true);
}
} catch (SQLException e) {
throw new TilerException(e);
}
}
@FunctionalInterface
public interface InTransaction {
void execute(Statement statement) throws SQLException;
}
我希望 lambda
参数能够与 jdbc 和 jooq 一起使用。
For jdbc 使用语句非常简单。例如像这样的 tutorail:
inTransaction(stmt -> {
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
});
为了在同一个事务上执行 jooq 查询,我必须获得一个上下文。我找到了一个 api 从 datasource/connection.
获取 DSLContext
我不清楚的是 if/how 从语句中创建一个 jooq DSLContext
?
如果你想从 jOOQ 获取查询字符串,你可以调用
String sqlString = query.getSQL()
然后在您的语句中使用此字符串:
stmt.executeUpdate(sqlString);
您描述的问题的解决方案
你可以用 jOOQ 的交易完成所有这些 API:
// Create this ad-hoc, or inject it, or whatever
DSLContext ctx = DSL.using(dataSource, dialect);
然后:
public void inJDBCTransaction(InJDBCTransaction lambda) {
ctx.transaction(config -> {
config.dsl().connection(connection -> {
try (Statement statement = connection.createStatement()) {
lambda.execute(statement);
}
});
});
}
public void inJOOQTransaction(InJOOQTransaction lambda) {
ctx.transaction(config -> lambda.execute(config.dsl()));
}
@FunctionalInterface
public interface InJDBCTransaction {
void execute(Statement statement) throws SQLException;
}
@FunctionalInterface
public interface InJOOQTransaction {
void execute(DSLContext ctx);
}
您的最终代码:
inJDBCTransaction(stmt -> {
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
});
inJOOQTransaction(ctx -> {
ctx.insertInto(EMPLOYEES).values(106, 20, "Rita", "Tez").execute();
ctx.insertInto(EMPLOYEES).values(107, 22, "Sita", "Singh").execute();
});
我不太相信需要对 jOOQ 和 JDBC 进行这种抽象。 jOOQ 从不向你隐藏 JDBC。使用 DSLContext.connection()
方法时,您始终可以访问 JDBC API,如上所示。所以,如上图:
- jOOQ 事务 API 完全符合您的计划。在事务上下文中包装一个 lambda,如果成功则提交,如果失败则回滚(您的版本回滚不起作用,因为它捕获了错误的异常)。
- 如果需要“JDBC 逃生口”,jOOQ 可以提供
旁注
在许多 RDBMS 中,您不希望 运行 查询静态 JDBC Statement
. You'll want to use PreparedStatement
,因为:
- 您将从执行计划缓存中获益(并减少对缓存的争用)
- 您将避免语法错误(如果您的实际查询是动态的)
- 您将避免 SQL 注入问题