将 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 注入问题