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) 隔 ...
随机推荐
- Web 服务器 low bandth DOS attack
https://www.owasp.org/images/0/04/Roberto_Suggi_Liverani_OWASPNZDAY2010-Defending_against_applicatio ...
- 安装hadoop集群服务器(hadoop1.2.1)
摘要:hadoop,一个分布式系统基础架构,可以充分利用集群的威力进行高速运算和存储.本文主要介绍hadoop的安装与集群服务器的配置. 准备文件: ▪ VMware11.0.0 ▪ Cen ...
- 总结ThinkPHP使用技巧经验分享(三)
add方法返回主键(id)的值在往数据表中添加数据时调用add方法,默认返回值就是刚添加的id值,就不用再去查询了. save方法返回值的判断在修改数据时,如果修改成功返回的是被修改的记录数0,1,2 ...
- Model Validation in ASP.NET Web API By Mike Wasson|July 20, 2012 268 of 294 people found this helpful
using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using ...
- Python快速建站系列-Part.Five.3-个人主页及资料页面
|版权声明:本文为博主原创文章,未经博主允许不得转载. 第五部分最后一节,完成个人主页里资料页面的个人资料的展示和修改功能,不过毕竟功能比较少,个人资料其实只有昵称一项,手动滑稽. 一如既往先写出来u ...
- CSS盒子模型元素实际宽度的计算
一旦为页面设置了恰当的 DTD,大多数浏览器都会按照上面的图示来呈现内容.然而 IE 5 和 6 的呈现却是不正确的.根据 W3C 的规范,元素内容占据的空间是由 width 属性设置的,而内容周围的 ...
- mysql数据库的备份与恢复
假定我们的目标数据库是 test, 表是 user.假定mysql的用户名和密码均为 root. 备份与恢复所用的程序分别是mysql软件包提供的 mysqldump 命令和 mysql 命令.思想很 ...
- js和jq添加新表格
javascript动态实现表格: function createTable(rowCount,cellCount){ var table = document.createElement('tabl ...
- Material Design Lite,简洁惊艳的前端工具箱 之 容器组件。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,博客地址为http://www.cnblogs.com/jasonnode/ .网站上有对应每一 ...
- Weblogic的架构
WebLogic管理控制台 WebLogic服务器提供了一个健壮的基于Web 的工具——管理控制台,它是执行上述任务的主要工具.通过管理控制台,你可以访问WebLogic管理服务.管理服务实现了 S ...