【问题标题】:Mix JOOQ query with JDBC transaction将 JOOQ 查询与 JDBC 事务混合
【发布时间】:2021-09-16 08:29:23
【问题描述】:

我有一个用例,我想将 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 一起使用。

对于 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 从数据源/连接中获取 DSLContext。
我不清楚的是是否/如何从声明中创建 jooq DSLContext

【问题讨论】:

  • 我不明白你的问题。使用 DSL.using 创建 DslContext 时,您必须将连接传递给配置。并且提交发生在连接上。
  • 我想传递一个语句而不是连接。我已经在使用该语句使用 jdbc 运行其他查询/更新。
  • 很抱歉,您的代码没有意义,因为您只能在每个事务的语句上执行。这将与自动提交相同
  • 除此之外还有一个未解决的问题:github.com/jOOQ/jOOQ/issues/3715
  • 我添加了一个如何使用该方法的示例。同一事务的多个查询中使用相同的语句。

标签: java jdbc jooq


【解决方案1】:

您描述的问题的解决方案

您可以使用 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 上运行查询。你会想改用PreparedStatement,因为:

  • 您将从执行计划缓存中获益(缓存争用更少)
  • 您将避免语法错误(以防您的实际查询是动态的)
  • 您将避免 SQL 注入问题

【讨论】:

    【解决方案2】:

    如果你想从 jOOQ 中获取查询字符串,你可以调用

    String sqlString = query.getSQL()
    

    然后在你的语句中使用这个字符串:

    stmt.executeUpdate(sqlString);
    

    【讨论】:

    • 谢谢,这是一个很好的更新解决方案。就我而言,我也有查询(jooq 解析结果并创建对象),但我不知道如何使用它。
    • 我会反其道而行之。对所有事情都使用 jOOQ。您还可以将 SQL 字符串传递给 jOOQ 以执行。
    猜你喜欢
    • 2012-12-12
    • 2019-02-09
    • 2016-02-12
    • 1970-01-01
    • 2020-06-12
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-15
    相关资源
    最近更新 更多