使用声明式事务

事务类本身就是一个拦截器,可以用注解的方式配置。方法内部的所有 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 操作是否都执行成功? 若都成功则事务提交,反之事务回滚。

JFinal - 事务实现的原理的更多相关文章

  1. TCC分布式事务的实现原理(转载 石杉的架构笔记)

    拜托,面试请不要再问我TCC分布式事务的实现原理![石杉的架构笔记] 原创: 中华石杉 目录 一.写在前面 二.业务场景介绍 三.进一步思考 四.落地实现TCC分布式事务 (1)TCC实现阶段一:Tr ...

  2. mysql 事务的实现原理

    开篇 相信大家都用过事务以及了解他的特点,如原子性(Atomicity),一致性(Consistency),隔离型(Isolation)以及持久性(Durability)等.今天想跟大家一起研究下事务 ...

  3. 面试刷题29:mysql事务隔离实现原理?

    mysql的事务是innodb存储引擎独有的,myisam存储引擎不支持事务. 事务最经典的例子就是转账了,事务要保证的是一组数据库的操作要么全部成功,要么全部失败.是为了保证高并发场景下数据的正确性 ...

  4. 【Srping】事务的执行原理(一)

    在使用事务的时候需要添加@EnableTransactionManagement注解来开启事务,那么就从@EnableTransactionManagement入手查看一下事务的执行原理. @Enab ...

  5. 【RocketMQ】事务的实现原理

    事务的使用 RocketMQ事务的使用场景 单体架构下的事务 在单体系统的开发过程中,假如某个场景下需要对数据库的多张表进行操作,为了保证数据的一致性,一般会使用事务,将所有的操作全部提交或者在出错的 ...

  6. 搞懂MySQL InnoDB事务ACID实现原理

    前言 说到数据库事务,想到的就是要么都做修改,要么都不做.或者是ACID的概念.其实事务的本质就是锁和并发和重做日志的结合体.那么,这一篇主要讲一下InnoDB中的事务到底是如何实现ACID的. 原子 ...

  7. RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)

    在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...

  8. RocketMQ源码分析之RocketMQ事务消息实现原理中篇----事务消息状态回查

    上节已经梳理了RocketMQ发送事务消息的流程(基于二阶段提交),本节将继续深入学习事务状态消息回查,我们知道,第一次提交到消息服务器时消息的主题被替换为RMQ_SYS_TRANS_HALF_TOP ...

  9. 一文快速搞懂MySQL InnoDB事务ACID实现原理(转)

    这一篇主要讲一下 InnoDB 中的事务到底是如何实现 ACID 的: 原子性(atomicity) 一致性(consistency) 隔离性(isolation) 持久性(durability) 隔 ...

随机推荐

  1. linux-----------shell的基础命令

    shell中截取字符串的方法有很多中, ${expression}一共有9种使用方法. ${parameter:-word} ${parameter:=word} ${parameter:?word} ...

  2. Url通配符映射

    原文:http://www.cnblogs.com/liukemng/p/3726897.ht 1.URL路径映射 1.1.对一个action配置多个URL映射: 我们把上一篇中的HelloWorld ...

  3. XPath 简介

    XPath 是一门在 XML 文档中查找信息的语言.XPath 用于在 XML 文档中通过元素和属性进行导航. 在学习之前应该具备的知识: 在您继续学习之前,应该对下面的知识有基本的了解: HTML ...

  4. struts---JSP界面验证码生成与验证

    之前想做一个随机验证码的功能,自己也搜索了一下别人写的代码,然后自己重新用struts2实现了一下,现在将我自己实现代码贴出来!大家有什么意见都可以指出来! 首先是生成随机验证码图片的action: ...

  5. HTML5中使用SVG

    SVG 即 Scalable Vector Graphics,是一种用来绘制矢量图的 HTML5 标签.你只需定义好XML属性,就能获得一致的图像元素. 使用SVG之前先将标签加入到HTML body ...

  6. java的基本数据类型有八种:

    1)四种整数类型(byte.short.int.long):    byte:8 位,用于表示最小数据单位,如文件中数据,-128~127    short:16 位,很少用,-32768 ~ 327 ...

  7. mysql 卸载 linux

    root@localhost ~]# rpm -qa | grep -i mysqlMySQL-client-5.5.52-1.linux2.6.x86_64MySQL-server-5.5.52-1 ...

  8. java代码优化

    优化通常包含两方面的内容:减小代码的体积,提高代码的运行效率. 1.尽量指定类的final修饰符 带有final修饰符的类是不可派生的.在Java核心API中,有许多应用final的例子,例如java ...

  9. 对于前端JS、Html、CSS的大小、位置是否影响网站的相应时间

    1.页面中大量的注释代码.空行会影响页面的加载速度 尽量去除打断的注释代码,及空行:尽可能的使用压缩后的JS.CSS文件,太小的文件没必要压缩 2.有人说CSS样式放在页面的开头,JS文件放在页面的结 ...

  10. 解决Window Azure: Failed to start Development Storage: the SQL Server instance ‘localhost\SQLExpress’ could not be found.

    运行Window Arzure 项目,报如下错误: Windows Azure Tools: Failed to initialize Windows Azure storage emulator. ...