事务管理
适用于 ✅ 开源版 ✅ 专业版 ✅ 企业版
jOOQ 可以与任何预先存在的事务模型(例如 JDBC、Spring、Jakarta EE)一起使用,或者可以选择提供自己的便捷事务 API。特别是
- 您可以使用第三方事务管理库,如 Spring TX
- 您可以使用来自容器的符合 JTA 的 Java EE 事务管理器。
- 您可以调用 JDBC 的
Connection.commit()
,Connection.rollback()
以及 JDBC 驱动上的其他方法,或使用 R2DBC 驱动上的等效方法。 - 您可以直接在数据库中发出供应商特定的
COMMIT
、ROLLBACK
和其他语句。 有关更多详细信息,请参见 关于事务语句的部分。 - 您可以使用 jOOQ 的事务 API。
使用 Spring Boot 时,它的 jOOQ 启动器已经预先配置了正确的 Spring 事务感知数据源,因此 Spring JDBC 事务可以与 jOOQ 一起开箱即用。
虽然 jOOQ 并不旨在取代以上任何一种方法,但它提供了一个简单的 API(以及相应的 SPI),为您提供 jOOQ 风格的程序流畅性来表达您的事务。 以下是一些 Java 示例,展示了如何使用 jOOQ 实现(嵌套)事务。 对于这些示例,我们使用 Java 8 语法。 但 Java 8 不是必需的。
create.transaction((Configuration trx) -> { AuthorRecord author = trx.dsl() .insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) .values("George", "Orwell") .returning() .fetchOne(); trx.dsl() .insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE) .values(author.getId(), "1984") .values(author.getId(), "Animal Farm") .execute(); // Implicit commit executed here });
// Examples use reactor, but you can use any other RS API, too create.transactionPublisher((Configuration trx) -> Mono .from(trx.dsl() .insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) .values("George", "Orwell") .returning()) .flatMap((AuthorRecord author) -> trx.dsl() .insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE) .values(author.getId(), "1984") .values(author.getId(), "Animal Farm")) // Implicit commit executed here });
请注意 lambda 表达式如何接收一个新的派生配置,该配置应在本地范围内使用
create.transaction((Configuration trx) -> { // Wrap configuration in a new DSLContext: trx.dsl().insertInto(...); trx.dsl().insertInto(...); // Or, reuse the new DSLContext within the transaction scope: DSLContext ctx = trx.dsl(); ctx.insertInto(...); ctx.insertInto(...); // ... but avoid using the scope from outside the transaction: create.insertInto(...); create.insertInto(...); });
create.transactionPublisher((Configuration trx) -> { // Wrap configuration in a new DSLContext: trx.dsl().insertInto(...); trx.dsl().insertInto(...); // Or, reuse the new DSLContext within the transaction scope: DSLContext ctx = trx.dsl(); ctx.insertInto(...); ctx.insertInto(...); // ... but avoid using the scope from outside the transaction: create.insertInto(...); create.insertInto(...); });
回滚
从您的事务代码(阻塞)抛出的任何未捕获的已检查或未检查异常,或在反应流(非阻塞)中的未处理错误将回滚事务到事务范围的开始。 如果您配置的 org.jooq.TransactionProvider
支持事务嵌套,则此行为将允许嵌套事务。 可以在此处看到一个例子
create.transaction((Configuration outer) -> { final AuthorRecord author = outer.dsl() .insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) .values("George", "Orwell") .returning() .fetchOne(); // Implicit savepoint created here try { outer.dsl() .transaction((Configuration nested) -> { nested.dsl() .insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE) .values(author.getId(), "1984") .values(author.getId(), "Animal Farm") .execute(); // Rolls back the nested transaction if (oops) throw new RuntimeException("Oops"); // Implicit savepoint is discarded, but no commit is issued yet. }); } catch (RuntimeException e) { // We can decide whether an exception is "fatal enough" to roll back also the outer transaction if (isFatal(e)) // Rolls back the outer transaction throw e; } // Implicit commit executed here });
// Examples use reactor, but you can use any other RS API, too create.transactionPublisher((Configuration outer) -> Mono .from(outer.dsl() .insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) .values("George", "Orwell") .returning()) .flatMap((AuthorRecord author) -> outer.dsl() // Implicit savepoint created here .transactionPublisher((Configuration nested) -> Mono .from(nested.dsl() .insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE) .values(author.getId(), "1984") .values(author.getId(), "Animal Farm")) // Rolls back the nested transaction .<Integer>flatMap(i -> { throw new RuntimeException("Oops"); })) // Implicit savepoint is discarded, but no commit is issued yet. .onErrorContinue((e, a) -> { log.info(e); })) // Implicit commit executed here );
仅阻塞 API 注意事项
虽然某些 org.jooq.TransactionProvider
实现(例如,基于 ThreadLocals 的实现,例如 Spring 或 JTA)可能允许您重用全局范围的 DSLContext 引用,但 jOOQ 事务 API 设计允许 TransactionProvider
实现要求您的事务代码使用新的、本地范围的 Configuration
。
事务代码包装在 jOOQ 的 org.jooq.TransactionalRunnable
或 org.jooq.TransactionalCallable
类型中
public interface TransactionalRunnable { void run(Configuration configuration) throws Exception; } public interface TransactionalCallable<T> { T run(Configuration configuration) throws Exception; }
可以将此类事务代码传递给 transaction(TransactionRunnable)
或 transactionResult(TransactionCallable)
方法。 一个使用 transactionResult()
的例子
int updateCount = create.transactionResult(configuration -> { int result = 0; DSLContext ctx = DSL.using(configuration); result += ctx.insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME).values("John", "Doe").execute(); result += ctx.insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME).values("Jane", "Doe").execute(); return result; });
TransactionProvider 实现
默认情况下,jOOQ 附带 org.jooq.impl.DefaultTransactionProvider
,它使用 JDBC java.sql.Savepoint
实现嵌套事务。 Spring Boot 将配置一个替代的 TransactionProvider
,它应该适用于大多数用例。 但是,您仍然可以实现自己的 org.jooq.TransactionProvider
并将其提供给您的 Configuration 以覆盖 jOOQ 的默认行为。 在此处可以看到一个使用 Spring 的 DataSourceTransactionManager
的简单示例实现
import static org.springframework.transaction.TransactionDefinition.PROPAGATION_NESTED; import org.jooq.Transaction; import org.jooq.TransactionContext; import org.jooq.TransactionProvider; import org.jooq.tools.JooqLogger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; public class SpringTransactionProvider implements TransactionProvider { private static final JooqLogger log = JooqLogger.getLogger(SpringTransactionProvider.class); @Autowired DataSourceTransactionManager txMgr; @Override public void begin(TransactionContext ctx) { log.info("Begin transaction"); // This TransactionProvider behaves like jOOQ's DefaultTransactionProvider, // which supports nested transactions using Savepoints TransactionStatus tx = txMgr.getTransaction(new DefaultTransactionDefinition(PROPAGATION_NESTED)); ctx.transaction(new SpringTransaction(tx)); } @Override public void commit(TransactionContext ctx) { log.info("commit transaction"); txMgr.commit(((SpringTransaction) ctx.transaction()).tx); } @Override public void rollback(TransactionContext ctx) { log.info("rollback transaction"); txMgr.rollback(((SpringTransaction) ctx.transaction()).tx); } } class SpringTransaction implements Transaction { final TransactionStatus tx; SpringTransaction(TransactionStatus tx) { this.tx = tx; } }
反馈
您对此页面有任何反馈吗? 我们很乐意听到您的声音!