事务类本身就是一个拦截器,可以用注解的方式配置。方法内部的所有 DML 操作都将在本次事务之内。
配置代码如下:
- @Before(Tx.class) public void savePost() {
- //...
- }
事务配置就是这么简单任性。
上述配置中为 savePost() 配置了事务也就是拦截器 Tx,当调用到 savePost() 的时候,是会进入到 Tx 的 intercept 方法中的:
主要代码如下:
- public void intercept(Invocation inv) {
- Config config = getConfigWithTxConfig(inv);
- if (config == null) config = DbKit.getConfig();
- Connection conn = config.getThreadLocalConnection();
- // 下面这段支持嵌套事务,可以忽略不看
- if (conn != null) {
- try {
- if (conn.getTransactionIsolation() < getTransactionLevel(config)) conn.setTransactionIsolation(getTransactionLevel(config));
- inv.invoke();
- return;
- } catch(SQLException e) {
- throw new ActiveRecordException(e);
- }
- }
- Boolean autoCommit = null;
- try {
- // 1. 建立数据库连接
- conn = config.getConnection();
- autoCommit = conn.getAutoCommit();
- config.setThreadLocalConnection(conn);
- // 2. 设置事务隔离级别
- conn.setTransactionIsolation(getTransactionLevel(config)); // conn.setTransactionIsolation(transactionLevel);
- // 3. 设置事务手动提交
- conn.setAutoCommit(false);
- // 4. 反射机制调用 savePost()
- inv.invoke();
- // 5. 事务提交
- conn.commit();
- } catch(NestedTransactionHelpException e) {
- if (conn != null) try {
- conn.rollback();
- } catch(Exception e1) {
- LogKit.error(e1.getMessage(), e1);
- }
- LogKit.logNothing(e);
- } catch(Throwable t) {
- // 6. 若有异常就回滚
- if (conn != null) try {
- conn.rollback();
- } catch(Exception e1) {
- LogKit.error(e1.getMessage(), e1);
- }
- throw t instanceof RuntimeException ? (RuntimeException) t: new ActiveRecordException(t);
- } finally {
- try {
- if (conn != null) {
- if (autoCommit != null) conn.setAutoCommit(autoCommit);
- conn.close();
- }
- } catch(Throwable t) {
- LogKit.error(t.getMessage(), t); // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
- } finally {
- config.removeThreadLocalConnection(); // prevent memory leak
- }
- }
- }
Tx.java 使用的是 JFinal 默认配置的事务隔离级别,是在 DbKit.java 中配置的
- public static final int DEFAULT_TRANSACTION_LEVEL = Connection.TRANSACTION_REPEATABLE_READ;
JFinal 还有几个拦截器,可以根据事务隔离级别的需求选用。
它们都继承与 Tx.java,唯一不同的就是事务隔离级别。
以 TxReadCommitted 为例。
继承 Tx.java,覆盖了 getTransactionLevel 方法,返回常量值,这个常量就代表了事务隔离级别。
- public class TxReadCommitted extends Tx {
- /**
- * A constant indicating that
- * dirty reads are prevented; non-repeatable reads and phantom
- * reads can occur. This level only prohibits a transaction
- * from reading a row with uncommitted changes in it.
- */
- private int TRANSACTION_READ_COMMITTED = 2;
- @Override protected int getTransactionLevel(com.jfinal.plugin.activerecord.Config config) {
- return TRANSACTION_READ_COMMITTED;
- }
- }
这种实现方式并没有使用拦截器。
跟踪代码最终追到 DbPro.java 如下代码中:
- boolean tx(Config config, int transactionLevel, IAtom atom) {
- Connection conn = config.getThreadLocalConnection();
- if (conn != null) { // Nested transaction support
- try {
- if (conn.getTransactionIsolation() < transactionLevel) conn.setTransactionIsolation(transactionLevel);
- boolean result = atom.run();
- if (result) return true;
- throw new NestedTransactionHelpException("Notice the outer transaction that the nested transaction return false"); // important:can not return false
- } catch(SQLException e) {
- throw new ActiveRecordException(e);
- }
- }
- Boolean autoCommit = null;
- try {
- // 1. 建立数据库连接
- conn = config.getConnection();
- autoCommit = conn.getAutoCommit();
- config.setThreadLocalConnection(conn);
- // 2. 设置事务隔离级别
- conn.setTransactionIsolation(transactionLevel);
- // 3. 设置事务手动提交
- conn.setAutoCommit(false);
- // 4. 所有 DML 操作是否都执行成功?
- boolean result = atom.run();
- // 5. 都成功:提交;不是都成功:回滚
- if (result) conn.commit();
- else conn.rollback();
- return result;
- } catch(NestedTransactionHelpException e) {
- if (conn != null) try {
- conn.rollback();
- } catch(Exception e1) {
- LogKit.error(e1.getMessage(), e1);
- }
- LogKit.logNothing(e);
- return false;
- } catch(Throwable t) {
- if (conn != null) try {
- conn.rollback();
- } catch(Exception e1) {
- LogKit.error(e1.getMessage(), e1);
- }
- throw t instanceof RuntimeException ? (RuntimeException) t: new ActiveRecordException(t);
- } finally {
- try {
- if (conn != null) {
- if (autoCommit != null) conn.setAutoCommit(autoCommit);
- conn.close();
- }
- } catch(Throwable t) {
- LogKit.error(t.getMessage(), t); // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
- } finally {
- config.removeThreadLocalConnection(); // prevent memory leak
- }
- }
- }
主要事务流程:
来源: http://www.cnblogs.com/xmsx/p/5844513.html