JFinal - 事务实现的原理
使用声明式事务
事务类本身就是一个拦截器,可以用注解的方式配置。方法内部的所有 DML 操作都将在本次事务之内。
配置代码如下:
@Before(Tx.class)
public void savePost(){
//...
}
事务配置就是这么简单任性。

声明式事务实现原理
上述配置中为 savePost() 配置了事务也就是拦截器 Tx,当调用到 savePost() 的时候,是会进入到 Tx 的 intercept 方法中的:
- 建立数据库连接;
- 设置事务隔离级别;
- 设置事务手动提交;
- 反射机制调用 savePost();
- 事务提交;
- 若事务失败,就回滚。
主要代码如下:
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
}
}
}
主要事务流程:
- 建立数据库连接;
- 设置事务隔离级别;
- 设置事务手动提交;
- 调用 Atom 的 run 方法,所有 DML 操作是否都执行成功? 若都成功则事务提交,反之事务回滚。
JFinal - 事务实现的原理的更多相关文章
- TCC分布式事务的实现原理(转载 石杉的架构笔记)
拜托,面试请不要再问我TCC分布式事务的实现原理![石杉的架构笔记] 原创: 中华石杉 目录 一.写在前面 二.业务场景介绍 三.进一步思考 四.落地实现TCC分布式事务 (1)TCC实现阶段一:Tr ...
- mysql 事务的实现原理
开篇 相信大家都用过事务以及了解他的特点,如原子性(Atomicity),一致性(Consistency),隔离型(Isolation)以及持久性(Durability)等.今天想跟大家一起研究下事务 ...
- 面试刷题29:mysql事务隔离实现原理?
mysql的事务是innodb存储引擎独有的,myisam存储引擎不支持事务. 事务最经典的例子就是转账了,事务要保证的是一组数据库的操作要么全部成功,要么全部失败.是为了保证高并发场景下数据的正确性 ...
- 【Srping】事务的执行原理(一)
在使用事务的时候需要添加@EnableTransactionManagement注解来开启事务,那么就从@EnableTransactionManagement入手查看一下事务的执行原理. @Enab ...
- 【RocketMQ】事务的实现原理
事务的使用 RocketMQ事务的使用场景 单体架构下的事务 在单体系统的开发过程中,假如某个场景下需要对数据库的多张表进行操作,为了保证数据的一致性,一般会使用事务,将所有的操作全部提交或者在出错的 ...
- 搞懂MySQL InnoDB事务ACID实现原理
前言 说到数据库事务,想到的就是要么都做修改,要么都不做.或者是ACID的概念.其实事务的本质就是锁和并发和重做日志的结合体.那么,这一篇主要讲一下InnoDB中的事务到底是如何实现ACID的. 原子 ...
- RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)
在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...
- RocketMQ源码分析之RocketMQ事务消息实现原理中篇----事务消息状态回查
上节已经梳理了RocketMQ发送事务消息的流程(基于二阶段提交),本节将继续深入学习事务状态消息回查,我们知道,第一次提交到消息服务器时消息的主题被替换为RMQ_SYS_TRANS_HALF_TOP ...
- 一文快速搞懂MySQL InnoDB事务ACID实现原理(转)
这一篇主要讲一下 InnoDB 中的事务到底是如何实现 ACID 的: 原子性(atomicity) 一致性(consistency) 隔离性(isolation) 持久性(durability) 隔 ...
随机推荐
- jquery选择器和基本语句
$("#aa"); //根据ID找 $(".aa"); //根据class找 $("div"); //根据标签名找 $("[id= ...
- git-----------------git:如何让git识别我修改了文件夹名字和文件名字的大小写问题。
修改每个项目里面的隐藏的.git文件里面的config文件.将箭头指的原本是true改成false.
- c++友元函数
c++友元函数分两类: 一://友员全居函数 /*#include <iostream>using namespace std;class aaa{ friend void prin ...
- (转)如何将本地git仓库上传到GitHub中托管+实践心得
Git——新手入门与上传项目到远程仓库GitHub(转) - Chen_s - 博客园http://www.cnblogs.com/Chenshuai7/p/5486278.html 注意的问题: 1 ...
- Asp.net上传文件后台通过二进制流发送到其他Url保存
实际情况一般有单独的站点存放静态文件,比如图片.office文档等.A站点的操作需要上传文件到B站点, 下面介绍一种方法通过System.Net.WebClient类的UploadData方法 . u ...
- [Phalcon] DI默认的服务
代码,说明一切 namespace Phalcon\Di\FactoryDefault; use Phalcon\Di\Service; use Phalcon\Di\FactoryDefault; ...
- SSM——(一)
入职第一天,项目经理要求利用SSM+MySQL做一个表单的CRUD:之前没用过mybatis,恶补了一下:http://www.jb51.net/article/70923.htm. spring三层 ...
- lua 基础 1
--1.1 Chunks--[[Chunk 是一系列语句,Lua 执行的每一块语句,比如一个文件或者交互模式下的每一行都是一个 Chunk.]] -- 1.2 全局变量--[[ 全局变量不需要声明,给 ...
- 通过servlet实现几个网站常用的功能
帮朋友写的小程序,由于功能比较简单所以就偷懒只使用了Servlet 一.JSP页面部分(这个部分的设置比较粗糙,主要是为了查看功能能否实现,如果需要向用户展示还得修饰一下) 1)功能页(所有需要后台实 ...
- Salesforce 执行顺序
在服务器上,Salesforce按以下顺序执行: 1,从数据库加载原始记录或初始化一个用于更新插入(upsert)语句的记录. 2,从请求加载新记录的字段值并覆盖旧的值. 如果请求来自一个标准的UI( ...