前言:有人问了我一个问题,就是说在网络中断的时候,JTA的全局事务管理,会不会回滚?当时说会回滚,但没给对方说清楚理由,也不太认同我的观点。现在总结一下。
今天一天都在看文档(也查了一些博客和网站),主要看EJB、JTA、JDBC、Mysql,来分享一下地址(全是官方网址,可能更具有权威):
http://docs.oracle.com/javaee/6/tutorial/doc/ 备注:虽然是java
EE 6的文档,但基本上不会差太多,主要看的是EJB、JTA
https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html
这个是J2SE 8的文档,主要看的关于事务的处理
https://dev.mysql.com/doc/refman/5.6/en/xa.html Mysql中的XA事务(分布式事务,必须实现XA协议,JTA多数据源全局事务管理中提到的两段式事务提交引擎,JTA的分布式事务必须使用支持XA协议的数据源,也叫作:nonemulated
data source)
https://dev.mysql.com/doc/refman/5.7/en/binary-log.html
Mysql中的binary-log,一个可以记录整个Mysql Server端的日志,redo log或者undo log,都只是记录InnoDB引擎的日志。
其他:
1,维基百科:https://en.wikipedia.org/wiki/Java_Transaction_API
2,Spring官方文档:36. Distributed Transactions with JTA
3,Oracle官方文档:7 Java Transaction API
看完之后,本宝宝坚持,会回滚!只是,有关于一些配置,尤其是不同数据源的事务配置,不管是代码层级,还是数据库服务配置,都不能错,缺一不可!
一、对问题的看法
在总结具体代码实施之前,我先总结一下对这个事儿的看法:
1,为什么我的回答,得不到明显认同,尽管我自己很确认这个答案!
第一点:可能是当时的语气表达太温和或者不太自信,尽管后来我再次确认,但似乎依然没有得到认可。第二点:对方可能在实际应用过程中,确实遇到了分布式事务没有回滚的情况。第三点:对方可能并不知道我在说什么,比如说我告诉他先获取一个上下文SessionContext ,再用UserTransaction接口,再去显示的开始事务,提交事务等,这样就就可以处理分布式事务(这是EJB实现分布式事务的方式,来源维基百科,Oracle相应的官方文档也有说明)第四点:可能真的是我理解错了
2,为什么我无法将我的观点表达清楚!
第一点:我看的是java EE规范中对于JTA、EJB中关于分布式事务处理的说明(纯英文官方文档),但我目前为止可能缺乏一个宏观的总结,我的表达可能缺乏条理,不够清晰、不够生动,所以,我现在进行总结。 第二点:对方一直问我最底层的实现,我都写出具体的代码了,但他问我JTA到底是怎么做到分布式事务处理的。对于那个层次上的具体机制,我确实目前还没有深入了解。
3,怎么去看待他问的关于网络中断,分布式事务会不会回滚的问题
第一点:EJB实现了java EE多个规范,其中一个很重要的功能就是实现JTA进行分布式事务处理,不可能只有他想到网络中断吧??? 那人家想到了,就一定会有解决方案,不管目前为止是否完善!
二、JTA事务管理
备注:对于一般的事务,不多说。主要说明一下分布式事务!
1,首先是EJB中对于分布式事务的实现
EJB支持两种形式的分布式事务,一种是基于Bean管理的,一种是基于容器管理的。简单说来,它们的主要区别就是编程式和声明式的区别。因为如果使用基于Bean管理的事务,那么代码会像这样:
begin transaction
...
update table-a
...
if (condition-x)
commit transaction
else if (condition-y)
update table-b
commit transaction
else
rollback transaction
begin transaction
update table-c
commit transaction
但如果是基于容器管理的事务,那么代码就会像这样:
@TransactionAttribute(NOT_SUPPORTED)
@Stateful
public class TransactionBean implements Transaction {
...
@TransactionAttribute(REQUIRES_NEW)
public void firstMethod() {...}
@TransactionAttribute(REQUIRED)
public void secondMethod() {...}
public void thirdMethod() {...}
public void fourthMethod() {...}
}
只是不管哪一种,都是通过JTA提供的UserTransaction接口而做的。它最基本的流程是:获取上下文—拿到UserTransaction接口—连接数据源—开启事务—提交事务—回滚事务!简单实例:
Context ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("jdbc/OracleDS");
Connection conn = ds.getConnection();
2,关于数据库的配置
前面也说到,分布式的事务,必须实现XA协议,而JTA的分布式事务,一定要使用XA协议的数据源。那么我们常用的mysql数据库,在处理分布式事务的时候,应该怎么配置???
两种情况,第一种:尽管我们从业务上划分了多个服务,但仍然在同一数据库;第二种:各个服务,并不在同一个数据库,但在同一个数据库服务器。PS:别问我连数据库服务器都不同怎么办,宝宝还没研究到那儿。
第一种情况:这个不需要对数据库有额外的配置,因为它还涉及不到多个数据源。这时候事务执行的简单过程如下:
1,先记录 undo/redo log,确保日志刷到磁盘上持久存储。
2,更新数据记录,缓存操作并异步刷盘。
3,提交事务,在 redo log 中写入 commit 记录。
注意点:undo/redo log ,只是记录InnoDB引擎的日志。另外一个也支持事务的引擎NDB,有一个ndb-log的配置。所以,尽管是一个数据库,但要保证事务,需要确认涉及到事务的表都是InnoDB引擎。另外:InnoDB支持128个回滚段,每个回滚段最多支持1023个并发数据修改事务,总共限制大约128K并发数据修改事务(只读事务不计入最大限制)。
第二种情况:需要额外的配置Mysql server的binary log和数据库隔离级别
简单说来,binary log是能够完整记录整个服务器操作的日志,当涉及到多数据源的时候,必须开启binary log。 第二点,处理分布式事务,必须选用可串行化隔离级别。
https://dev.mysql.com/doc/refman/5.6/en/xa.html
我目前对于以上所涉及到的文档,得出的理解是目前的样子。如果有不一致的,还望指导一二!
三、总结
我有那么瞬间都在怀疑我看的官方文档到底是不是真的! 还有一个就是SpringMVC注解的实现,我告诉他我实现的自定义注解,是通过反射去做的,所以我认为是反射。但我看他那意思好像是我又错了,因为让我再次确认了一下。 我好疑惑,其实,如果我说的不对,那就告诉我正确的是什么,或者就说我答的不对也行,我问他吧,又不跟我说。
唉,说来说去,还是自己总结的不够深入,知识体系、宏观体系上的逻辑关联性,还需要进一步加强,加油啦!