经典分布式事务,是相对互联网中的柔性分布式事务而言,其特性为ACID原则,包括原子性(Atomictiy)、一致性(Consistency)、隔离性(Isolation)、持久性(Durabilit):
XA是啥?
XA是由X/Open组织提出的分布式事务的架构(或者叫协议)。XA架构主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。也就是说,在基于XA的一个事务中,我们可以针对多个资源进行事务管理,例如一个系统访问多个数据库,或即访问数据库、又访问像消息中间件这样的资源。这样我们就能够实现在多个数据库和消息中间件直接实现全部提交、或全部取消的事务。XA规范不是java的规范,而是一种通用的规范,
目前各种数据库、以及很多消息中间件都支持XA规范。
JTA是满足XA规范的、用于Java开发的规范。所以,当我们说,使用JTA实现分布式事务的时候,其实就是说,使用JTA规范,实现系统内多个数据库、消息中间件等资源的事务。
JTA(Java Transaction API),是J2EE的编程接口规范,它是XA协议的JAVA实现。它主要定义了:
本篇以Spring MVC+Maven+Atomikos+Druid+MyBatis演示分布式事务的实现。
Mave 的pom.xml
1.8
8.0.11
1.1.17
5.1.8.RELEASE
3.2.12
5.0.0
1.9.4
1.5.4
1.1
3.2.0
1.2.0
1.2.17
4.12
3.2.4
org.mybatis
mybatis
${mybatise.version}
org.mybatis
mybatis-spring
${mybatis.spring}
com.atomikos
atomikos-util
${atomikos.version}
com.atomikos
transactions
${atomikos.version}
com.atomikos
transactions-jta
${atomikos.version}
com.atomikos
transactions-jdbc
${atomikos.version}
com.atomikos
transactions-api
${atomikos.version}
javax.transaction
jta
${jta.version}
cglib
cglib-nodep
${cglib.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-orm
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.aspectj
aspectjweaver
${aspectjweaver.version}
aspectj
aspectjrt
${aspectjrt.version}
cglib
cglib
${cglib.version}
mysql
mysql-connector-java
${mysql.version}
com.alibaba
druid
${druid.version}
junit
junit
${junit.version}
test
spring-application-context.xml
spring-mybatis-atomikos-druid.xml
${jdbc.driverClassName}
10
3
100
60000
60000
300000
SELECT 'x'
true
false
false
stat
dataSourceOne
${jdbc.url}
${jdbc.username}
${jdbc.password}
dataSourceTwo
${jdbc.two.url}
${jdbc.two.username}
${jdbc.two.password}
jdbc.properties
#mysql 6.*以上
jdbc.driverClassName = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&pinGlobalTxToPhysicalConnection=true&useSSL=false
jdbc.username =root
jdbc.password =root
jdbc.two.url = jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&pinGlobalTxToPhysicalConnection=true&useSSL=false
jdbc.two.username =root
jdbc.two.password =root
jta.properties
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name=tm.release.out
com.atomikos.icatch.log_base_name=tm.releaselog
com.atomikos.icatch.tm_unique_name=com.atomikos.spring.jdbc.tm.release
com.atomikos.icatch.console_log_level=INFO
TestInsert.java
@ContextConfiguration(value = {"classpath:spring-application-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TestInsert {
private Logger logger = LoggerFactory.getLogger(TestInsert.class);
@Autowired
private BatchInsertService batchInsertService;
@Test
public void insert(){
long startTime = System.currentTimeMillis();
User user = new User();
user.setName("User_"+(int)(Math.random()*100));
user.setAge((int)(Math.random()*100));
CustInfo info = new CustInfo();
info.setPhone("123456789"+(int)(Math.random()*100));
batchInsertService.insert(user,info);
long endTime = System.currentTimeMillis();
logger.info("共耗时:{}毫秒",endTime -startTime);
}
}
BatchInsertService.java
@Service
public class BatchInsertService {
private Logger logger = LoggerFactory.getLogger(BatchInsertService.class);
@Autowired
private UserService userService;
@Autowired
private CustInfoService custInfoService;
@Transactional(rollbackFor= {Exception.class,RuntimeException.class})
public void insert(User user,CustInfo custInfo) {
int insertUser = userService.insert(user);
logger.info("insertUser={}",insertUser);
int insertCustInfo = custInfoService.insert(custInfo);
logger.info("insertCustInfo={}",insertCustInfo);
}
}
UserService.java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public int insert(User record) {
int result = userMapper.insert(record);
return result;
}
}
CustInfoService.java
@Service
public class CustInfoService {
@Autowired
private CustInfoMapper custInfoMapper;
public int insert(CustInfo record) {
int result = custInfoMapper.insert(record);
long now = System.currentTimeMillis();
// 模拟一个异常
if (now % 2 == 0) {
throw new RuntimeException("CustInfoMapper throws test insert exception");
}
return result;
}
}
Mapper和Bean等就不列出来了,完成的示例工程在github:https://github.com/fenglibin/DruidWithAtomikos