SpringBoot之JPA框架下如何使用JTA——分布式事务解决方案
以前,笔者写过一篇博客,支付宝DTS方案,当然,只是仅仅是简单讨论了下分布式事务的解决方案。PS:笔者看了下相关评论,发现由于太简单,被不少人Diss了一通。
最近,笔者在自己的工程上,试图一次性解决分布式事务问题。
笔者自身的工程,目前是Springboot作为基本框架,以JPA作为数据库操作工作。笔者首先想到的,是如何利用现有框架,以及成熟的方案,做一个分布式解决方案。
然后,查了一段时间,发现JTA解决方案。
然后发现Atomikos,就用Atomikos作为核心解决方案。
发现JTA解决分布式方案的思想,是对事务进行托管。因为单独一个事务,本身是ACID的;两个事务,却不能做到同时ACID。可行方案是TCC或两阶段提交(或三阶段提交)。而对事务进行托管,实际上是让托管程序完成TCC或两阶段提交。
关于分布式解决方案,如果博友想学习,可以参考田守枝的技术博客(分布式事务概述_分布式事务教程_田守枝Java技术博客本文介绍了分布式事务的基础概念,包括本地事务、DTP模型,XA两阶段提交,基于BASE理论的柔性事务等
http://www.tianshouzhi.com/api/tutorials/distributed_transaction/383),笔者认为是关于分布式事务里极好的博客。大家可以对分布式事务进行整体认识。
但本篇博客的精髓,在于实现Springboot框架下的分布式事务。
笔者参考了大部分技术博客,发现大部分博客介绍JTA,会把JTA作为一个Repository方式进行增删改查。这种方式的优点是可以简单完成数据库的存储,但缺点是不能较好使用JPA。
一个理想的方法是:单独使用JPA,让JPA可以自用操作数据库。
笔者的实现步骤是:
第一步,更改pom.xml文件,添加依赖库。
<!-- distributed transaction support -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jta</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jdbc</artifactId>
</dependency>
第二步,更改Persistence.xml文件;该文件可以用EntityManagerFactory生成EntityManager。
<!--定义持久单元 -->
<persistence-unit name="xxx" transaction-type="JTA">
<property name="org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform" value="路径.AtomikosJtaPlatform" />
注意:这里面需要做的是:
1)transaction-type改为JTA;
2)在hibernate4.3以后,JtaPlatform的路径为org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform,之前为hibernate.transaction.jta.platform;
3)AtomikosJtaPlatform的路径必须存在。
第三步,生成AtomikosJtaPlatform文件。
public class AtomikosJtaPlatform extends AbstractJtaPlatform {
private static final long serialVersionUID = 1L;
static TransactionManager transactionManager;
static UserTransaction transaction;
@Override
protected TransactionManager locateTransactionManager() {
return transactionManager;
}
@Override
protected UserTransaction locateUserTransaction() {
return transaction;
}
}
注意transactionManager即对事务托管的管理器;transaction对应不同事务。此方案即可看到事务的管理。
第四步,编写可执行程序。
EntityManager em0 = null; //调用自己的方法生成
EntityManager em1 = null; //调用自己的方法生成
try {
//选用任意一个EntityManager的事务即可
em0.getTransaction().begin();
//不同事务操作
em0.merge(entity0);
em1.merge(entity1);
//选用原有EntityManager提交
em0.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
try {
//正确状态进行事务回滚
if (em0.getTransaction().isActive() && em0.getTransaction().getRollbackOnly()) {
em0.getTransaction().rollback();
}
} catch (Exception ex) {
ex.printStackTrace();
}
} finally {
if (em0 != null) {
em0.close();
em0 = null;
}
if (em1 != null) {
em1.close();
em1 = null;
}
}
在实际执行中,打开一个事务即可。
调用该方案之后,发现运行出现如下错误:
Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine suitable jdbc url
经过多次试验以后,发现该问题是由JTA库造成的。
解决方案如下:
在Springboot启动对应的yml或propeties库中加入如下的配置。(Properties文件需要换格式修改)
spring:
# JTA
jta:
enabled: false
该方法可以让JTA不随Springboot启动,从而让该错误不发生。
使用者可以在自己的程序中,灵活使用JPA。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)