JFinal学习--08事务

JFinal支持事务的操作,这里引用JFinal手册中的一段代码:

boolean succeed = Db.tx(new IAtom(){
    public boolean run() throws SQLException {
        int count = Db.update("update account set cash = cash - ? where id = ?", 100, 123);
        int count2 = Db.update("update account set cash = cash + ? where id = ?", 100, 456);
        return count == 1 && count2 == 1;
    }
});

我们可以通过如上代码中的方法,执行两个更新操作,其中只要一个操作出现异常,就会回滚。那么,要做到如上的需求,代码中是如何保证的呢?

首先,我们看 IAtom这个接口,它里面定义了一个run() 方法,我们对数据库操作需要事务支持的话,就可以代码写在这里面。写在这里后,又是如何做到事务的呢?接着往下看。

Db.tx(IAtom atom) 方法调用了DbPro类的tx(IAtom atom) 方法,这个方法,就是对数据库的操作,以及事务的封装。

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 {
            conn = config.getConnection();
            autoCommit = conn.getAutoCommit();
            config.setThreadLocalConnection(conn);
            conn.setTransactionIsolation(transactionLevel);
            conn.setAutoCommit(false);
            boolean result = atom.run();
            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.这里会先通过
Connection conn = config.getThreadLocalConnection();
获取存储在ThreadLocal中的连接,如果conn不为空,我们则认为当前的数据库操作处于事务中,直接执行数据库操作,即atom.run();

这里有两点需要指出:
★在一个事务开始的时候,会将当前数据库连接存储到 com.jfinal.plugin.activerecord.Config 类的 threadLocal属性中,ThreadLocal这个类的作用是为每个线程维护自己的变量,在这里不同的线程之间会创建各自的连接,互不影响。
★atom.run() 方法,会依次执行数据库的操作,在具体的数据库操作中,如update:

public boolean update(String tableName, String primaryKey, Record record) {
        Connection conn = null;
        try {
            conn = config.getConnection();
            return update(config, conn, tableName, primaryKey, record);
        } catch (Exception e) {
            throw new ActiveRecordException(e);
        } finally {
            config.close(conn);
        }
    }

这里会执行 config.close(conn) 关闭连接的操作,进去close方法中看:

    public final void close(Connection conn) {
        if (threadLocal.get() == null)      // in transaction if conn in threadlocal
            if (conn != null)
                try {conn.close();} 
                catch (SQLException e) {
                    throw new ActiveRecordException(e);、
                }
    }

上面方法中,会判断ThreadLocal中是否存在连接,如果不存在的话,则关闭当前连接–相当于当前数据库操作不是在事务中的话,就关闭连接。

2.接上面的1,如果从ThreadLocal中没有获取到连接,则将当前连接存入ThreadLocal中,且开启事务。执行boolean result = atom.run();,如果返回值result 为false 则回滚,否则提交事务。最后操作完成后,关闭连接,移除ThreadLocal中的连接。

你可能感兴趣的:(JFinal学习)