JFinal使用事务及其原理

来源:互联网 发布:中国东南亚战略知乎 编辑:程序博客网 时间:2024/05/16 09:48

使用声明式事务

事务类本身就是一个拦截器,可以用注解的方式配置。方法内部的所有 DML 操作都将在本次事务之内。

配置代码如下:

@Before(Tx.class)public void savePost(){    //...}

事务配置就是这么简单任性。

 

回到顶部

声明式事务实现原理

上述配置中为 savePost() 配置了事务也就是拦截器 Tx,当调用到 savePost() 的时候,是会进入到 Tx 的 intercept 方法中的:

  1. 建立数据库连接;
  2. 设置事务隔离级别;
  3. 设置事务手动提交;
  4. 反射机制调用 savePost();
  5. 事务提交;
  6. 若事务失败,就回滚。

主要代码如下:

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        }    }}

主要事务流程:

  1. 建立数据库连接;
  2. 设置事务隔离级别;
  3. 设置事务手动提交;
  4. 调用 Atom 的 run 方法,所有 DML 操作是否都执行成功? 若都成功则事务提交,反之事务回滚。

原创粉丝点击