现在的分布式事务处理不在需要依赖容器了~~~当年的分布式事务我还是基于Jboss和Weblogic作为事务协调器来处理.
- JTA,即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。JTA 为 J2EE 平台提供了分布式事务服务,它隔离了事务与底层的资源,实现了透明的事务管理方式.但是JTA是应用在数据层面的,且需要应用服务器的支持cuiyaonan2000@163.com
-
Atomikos 是一个为Java平台提供增值服务的并且开源类事务管理器(即Transaction Manager)
另外就不直接贴代码了,换个角度,研究下这个分布式事务cuiyaonan2000@163.com
Original Way Of Handle Database在刚开始入门Java的时候我们知道通过Class.forname("数据库驱动类")是连接数据库的第一步.其实现在Spring,Druid等等都是围绕这些来封装的.那么多个数据源,分布式事务肯定也是基于这些.所以我们从最原始的数据库操作开始来理解多数据源的处理cuiyaonan2000@163.com
翻出了珍藏多年的java代码~~~
package cui.yao.nan.moredatabasetransaction.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @author cuiyaonan2000@163.com * @des: TODO * @date 2009年7月9日 上午10:46:38 * @package cui.yao.nan.moredatabasetransaction.util */ public class Test { public static void main(String[] args) throws SQLException { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 注册driver Class.forName("com.mysql.jdbc.Driver"); // 建立数据库对象 conn = DriverManager.getConnection("127.0.0.1", "cuiyaonan2000@163.com", "cuiyaonan2000@163.com"); // 默认是自动提交的,关闭自动提交 conn.setAutoCommit(false); // 建立操作对象 stmt = conn.createStatement(); // 结果集 rs = stmt.executeQuery("select * from information"); // 提交 conn.commit(); } catch (Exception e) { // 回滚 conn.rollback(); } finally { // 依次关闭结果集,操作对象,数据库对象 if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (conn != null) { conn.close(); } } } }
从上面的代码可以看到,最原始的数据库操作分为如下几部:
- 加载数据库驱动driver
- 建立数据库连接connection
- 关闭Connection事务自动提交,改为手动提交
- 创建statement
- 通过statement执行sql
- 通过Connection对象提交对数据库的操作,如果有异常就用Connection对象回滚(另外注意这里:通常我们的代码开启事务只需要创建一个事务对象,并入住数据源就可以了.从原始代码种类我们可以看到,事务只跟数据源有关联,跟操作无关cuiyaonan2000@16.com)
- 依次关闭ResultSet,statement,connection
是不是~~很简单,如果原始的代码去让你连接10个数据库感觉都不是问题了~~~或者将10个数据库的操作放到一个方法里,进行统一的回滚和提交也非常简单.cuiyaonan2000@163.com
The Way Of Mybatis Connect Database以往我们配置Mybatis需要做如下的事情:
- 创建Mapper接口
- 创建对应Mapper接口的XML文件
- 让系统扫描Mapper接口路径与XML文件路径,将两者结合起来,创建Mappe接口的代理实现类
Mybatis内部的做法是:
- 创建一个SqlSessionFactoryBean,设置datasource与xml文件路径
- 通过SqlSessionFactoryBean创建SqlSessionFactory
- 通过SqlSessionFactory创建SqlSessionTemplate
- 把这个SqlSessionTemplate“关联”到mapper接口
具体参考如下(另外通过代码我们可以看到事务的开启仅需要将数据源加入就可以了.跟SqlSessionTemplate没有直接关联.)
package nan.yao.cui.dataSources.conf; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; /** * * @Title: DataSourceConfig.java * @Package nan.yao.cui.dataSources * @Description: 多数据源配置类 * @author cuiyaonan2000@163.com * @date 2019年8月6日 下午3:03:43 * @version V1.0 */ @Configuration public class DataSourceConfig { @Bean(name = "readTestDb" ) @ConfigurationProperties(prefix = "spring.datasource.druid.read-test-db") public DataSource readTestDb() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "writeTestDb") @ConfigurationProperties(prefix = "spring.datasource.druid.write-test-db") public DataSource writeTestDb() { return DruidDataSourceBuilder.create().build(); } /** * 动态数据源: 通过AOP在不同数据源之间动态切换 * @return */ @Primary @Bean(name = "dynamicDataSource") public DynamicDataSource dataSource() { //本来构造函数是可以不用私有化的,但是为了使用动态增加就弄了 // DynamicDataSource dynamicDataSource = new DynamicDataSource(); DynamicDataSource dynamicDataSource = DynamicDataSource.getDynamicDataSource(); // 默认数据源 dynamicDataSource.setDefaultTargetDataSource(readTestDb()); // 配置多数据源 Map dsMap = new HashMap(); dsMap.put("readTestDb", readTestDb()); dsMap.put("writeTestDb", writeTestDb()); dynamicDataSource.setTargetDataSources(dsMap); return dynamicDataSource; } /** * 配置@Transactional注解事物 * @return */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } }PlatformTransactionManager
原生的Java事务开启是基于Connection.但是Mybatis或者Hibernate后者我们要使用的JTA他们的事务管理都不一样.所以Spring为我们提供了PlatformTransactionManager来想下兼容cuiyaonan2000@163.com
PlatformTransactionManager接口package org.springframework.transaction; import org.springframework.lang.Nullable; public interface PlatformTransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }TransactionDefinition
该类主要是事务的常量参数信息,包含了超时设置,隔离级别设置等等
package org.springframework.transaction; import java.sql.Connection; import org.springframework.lang.Nullable; public interface TransactionDefinition { int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6; int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; int TIMEOUT_DEFAULT = -1; int getPropagationBehavior(); int getIsolationLevel(); int getTimeout(); boolean isReadOnly(); @Nullable String getName(); }TransactionStatus
事务处理中的状态点
package org.springframework.transaction; import java.io.Flushable; public interface TransactionStatus extends SavepointManager, Flushable { boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); @Override void flush(); boolean isCompleted(); }不同框架的PlatformTransactionManager实现
像是Jdbc,Jta这些都是java中的标准,所以spirng-tx是自带了针对Jdbc和Jta的事务实现.如果你的工程引入了Jpa或者Hibernate,你会看到JpaTransactionManager 和 HibernateTransactionManager.这里没有引入Jpa和Hibernate包所以你看不到cuiyaonan2000@163.com
关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入(只要你引入,系统就会自动创建一个事务管理器) DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
我也可以自己创建事务管理器,且用@Bean 标注,则框架不会重新实例化其他的PlatformTransactionManager 实现类cuiyaonan2000@163.com。
设置默认事务管理器如果你的工程中有多个事务管理器,且在@Transactional中没有通过value指明哪个事务管理器则会报错cuiyaonan2000@163.com
所以通过实现接口TransactionManagementConfigurer 来指明默认的事务管理器:
package cui.yao.nan.moredatabasetransaction.util; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.TransactionManagementConfigurer; /** * @author cuiyaonan2000@163.com * @des: TODO * @date 2021年7月9日 下午3:28:00 * @package cui.yao.nan.moredatabasetransaction.util */ public class DefaultTransactonManager implements TransactionManagementConfigurer { @Override public PlatformTransactionManager annotationDrivenTransactionManager() { // TODO Auto-generated method stub //如下返回事务的实体Bean就可以了 return null; } }DataSource
同理DataSource也是一个接口,关于它的实现类根据不同的框架和实现方案可以看到如下的: