JTA/XA全局事务解决方案atomikos

Atomikos

Atomikos公司官方网址为:https://www.atomikos.com/。其旗下最著名的产品就是事务管理器。产品分两个版本:

TransactionEssentials:开源的免费产品ExtremeTransactions:上商业版,需要收费。

这两个产品的关系如下图所示:

TransactionEssentials:

1、实现了JTA/XA规范中的事务管理器(Transaction Manager)应该实现的相关接口,如:

UserTransaction实现是com.atomikos.icatch.jta.UserTransactionImp,用户只需要直接操作这个类

Tran我爱线报网每日持续更新海量各大内部创业教程sactionManager实现是com.atomikos.icatch.jta.UserTransactionManager

Transaction实现是com.atomikos.icatch.jta.TransactionImp

2、针对实现了JDBC规范中规定的实现了XADataSource接口的数据库连接池,以及实现了JMS规范的MQ客户端提供一层封装。

在上一节我们讲解JTA规范时,可以看之前文章。提到过XADataSource、XAConnection等接口应该由资源管理器RM来实现,而Atomikos的作用是一个事务管理器(TM),并不需要提供对应的实现。而Atomikos对XADat我爱线报网每日持续更新海量各大内部创业教程aSource进行封装,只是为了方便与事务管理器整合。封装XADataSource的实现类为AtomikosDataSourceBean。典型的XADataSource实现包括:

1、mysql官方提供的

com.mysql.jdbc.jdbc2.optional.MysqlXADataSource

2、阿里巴巴开源的druid连接池,对应的实现类为

com.alibaba.druid.pool.xa.DruidXADataSource

3、tomcat-jdbc连接池提供的

org.apache.tomcat.jdbc.pool.XADataSource

而其他一些常用的数据库连接池,如dbcp、dbcp我爱线报网每日持续更新海量各大内部创业教程2或者c3p0,目前貌似尚未提供XADataSource接口的实现。如果提供给AtomikosDataSourceBean一个没有实现XADataSource接口的数据源,如c3p0的ComboPooledDataSource,则会抛出类似以下异常:

ExtremeTransactions在TransactionEssentials的基础上额外提供了以下功能:

支持TCC:这是一种柔性事务

支持通过RMI、IIOP、SOAP这些远程过程调用技术,进行事务传播。

直接使用TransactionEssentials的API

新建一个maven项目,并添加如下依赖:

<dependency> <groupId>c我爱线报网每日持续更新海量各大内部创业教程om.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.39</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <vers我爱线报网每日持续更新海量各大内部创业教程ion>1.1</version> </dependency>

新建mysql数据库表

需要注意的是,在mysql中,只有innodb引擎才支持XA事务,所以这里显式的指定了数据库引擎为innodb。

— 新建数据库db_user; create database db_user; — 在db_user库中新建user表 create table db_user.user(id int AUTO_INCREMENT PRIMARY KEY,name varchar(50)) engine=innodb; — 新建数据库db_account; create database db_account; — 在db_account库中我爱线报网每日持续更新海量各大内部创业教程新建account表 create table db_account.account(user_id int,money double) engine=innodb;

db_user库和db_account库是位于同一个mysql实例中的。

代码示例

在使用了事务管理器之后,我们通过atomikos提供的UserTransaction接口的实现类

com.atomikos.icatch.jta.UserTransactionImp来开启、提交和回滚事务。而不再是使用java.sql.Connection中的setAutoCommit(false)的方式来开启事务。其他JTA规范中定义的接口,开发人员并不需要直接使我爱线报网每日持续更新海量各大内部创业教程用。import com.atomikos.icatch.jta.UserTransactionImp; import com.atomikos.jdbc.AtomikosDataSourceBean; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; importjava.ut我爱线报网每日持续更新海量各大内部创业教程il.Properties;public class AtomikosExample { private static AtomikosDataSourceBean createAtomikosDataSourceBean(String dbName) { // 连接池基本属性 Properties p = new Properties(); p.setProperty(“url”, “jdbc:mysql://localhost:3306/” + dbName); p.setProperty(“user”, “root”); p.setProperty(“password”, “”); // 使用At我爱线报网每日持续更新海量各大内部创业教程omikosDataSourceBean封装com.mysql.jdbc.jdbc2.optional.MysqlXADataSource AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); //atomikos要求为每个AtomikosDataSourceBean名称,为了方便记忆,这里设置为和dbName相同 ds.setUniqueResourceName(dbName); ds.setXaDataSourceClassName(“com.mysql.jdbc.jdbc2.optional.MysqlXADataSource”我爱线报网每日持续更新海量各大内部创业教程); ds.setXaProperties(p); return ds; } public static void main(String[] args) { AtomikosDataSourceBean ds1 = createAtomikosDataSourceBean(“db_user”); AtomikosDataSourceBean ds2 = createAtomikosDataSourceBean(“db_account”); Connection conn1 = null; Connection conn2 = null; Prepar我爱线报网每日持续更新海量各大内部创业教程edStatement ps1 =null; PreparedStatement ps2 = null; UserTransaction userTransaction = new UserTransactionImp(); try { // 开启事务 userTransaction.begin(); // 执行db1上的sql conn1 = ds1.getConnection(); ps1 = conn1.prepareStatement(“INSERT into user(name) VALUES (?)”, Statement.RETURN_GENERATED_KEYS); 我爱线报网每日持续更新海量各大内部创业教程 ps1.setString(1, “tianshouzhi”); ps1.executeUpdate(); ResultSet generatedKeys = ps1.getGeneratedKeys(); int userId = –1; while (generatedKeys.next()) { userId = generatedKeys.getInt(1);// 获得自动生成的userId } // 模拟异常 ,直接进入catch代码块,2个都不会提交 // int i=1/0; // 执行db2上的sqlconn2 = ds2.getConnection(); 我爱线报网每日持续更新海量各大内部创业教程ps2 = conn2.prepareStatement(“INSERT into account(user_id,money) VALUES (?,?)”); ps2.setInt(1, userId); ps2.setDouble(2, 10000000); ps2.executeUpdate(); // 两阶段提交 userTransaction.commit(); } catch (Exception e) { try { e.printStackTrace(); userTransaction.rollback(); } catch(Syste我爱线报网每日持续更新海量各大内部创业教程mException e1) { e1.printStackTrace(); } }finally { try { ps1.close(); ps2.close(); conn1.close(); conn2.close(); ds1.close(); ds2.close(); } catch (Exception ignore) { } } } }

运行上述的代码,数据库表插入相应的数据。将代码中间异常注释去掉,模拟异常,再运行一遍,发现两个库都没有插入数据。

TransactionEssent我爱线报网每日持续更新海量各大内部创业教程ials与spring、mybatis整合

在pom中添加以下依赖

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency> <de我爱线报网每日持续更新海量各大内部创业教程pendency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency>

新建User实体

public class User { private int id; private String name; // setters and get我爱线报网每日持续更新海量各大内部创业教程ters }

新建Account实例

public class Account { private int userId; private double money; // setters and getters }

新建UserMapper接口,为了方便,这里使用了mybatis 注解方式,没有编写映射文件,作用是一样的

public interface UserMapper { @Insert(“INSERT INTO user(id,name) VALUES(#{id},#{name})”) @Options(useGeneratedKeys = true, keyColumn = “id”, keyProperty = “id”)我爱线报网每日持续更新海量各大内部创业教程 public void insert(User user); }

新建AccountMapper接口

public interface AccountMapper {    @Insert(“INSERT INTO account(user_id,money) VALUES(#{userId},#{money})”)     public void insert(Account account); }

新建使用JTA事务的bean,注意在使用jta事务的时候,依然可以使用spring的声明式事务管理

public class JTAService { @Autowired privateUserMapper 我爱线报网每日持续更新海量各大内部创业教程userMapper;//操作db_user库 @Autowired private AccountMapper accountMapper;//操作db_account库 @Transactional public void insert() { User user = new User(); user.setName(“wangxiaoxiao”); userMapper.insert(user); // int i = 1 / 0;//模拟异常,spring回滚后,db_user库中user表中也不会插入记录 Account account = newAccount(); acco我爱线报网每日持续更新海量各大内部创业教程unt.setUserId(user.getId()); account.setMoney(123456789); accountMapper.insert(account); } }

编写配置文件spring-atomikos.xml

<?xml version=”1.0″ encoding=”UTF-8″?> <beans xmlns=“http://www.springframework.org/schema/beans” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:tx=“http://w我爱线报网每日持续更新海量各大内部创业教程ww.springframework.org/schema/tx” xsi:schemaLocation=“http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd”> <!–==========针对两个库,各配置一个Atomikos我爱线报网每日持续更新海量各大内部创业教程DataSourceBean,底层都使用MysqlXADataSource=====================–> <!–配置数据源db_user–> <bean id=“db_user” class=“com.atomikos.jdbc.AtomikosDataSourceBean” init-method=“init” destroy-method=“close”> <property name=“uniqueResourceName” value=“ds1” /> <property name=“xaDataSourceClassName” value=“com.mysql.jdbc.jdbc2.op我爱线报网每日持续更新海量各大内部创业教程tional.MysqlXADataSource” /> <property name=“xaProperties”> <props> <prop key=“url”>jdbc:mysql://localhost:3306/db_user</prop> <prop key=“user”>root</prop> <prop key=“password”></prop> </props> </property> </bean> <!–配置数据源db_account–> <bean id=“db_account” class=“com.atomikos.jdbc.AtomikosDataSourceBean” init-metho我爱线报网每日持续更新海量各大内部创业教程d=“init” destroy-method=“close”> <property name=“uniqueResourceName” value=“ds2” /> <property name=“xaDataSourceClassName” value=“com.mysql.jdbc.jdbc2.optional.MysqlXADataSource” /> <property name=“xaProperties”> <props> <prop key=“url”>jdbc:mysql://localhost:3306/db_account</prop> <prop key=“user”>root</prop> <prop我爱线报网每日持续更新海量各大内部创业教程 key=“password”></prop> </props> </property> </bean> <!–=============针对两个数据源,各配置一个SqlSessionFactoryBean============ –> <bean id=“ssf_user” class=“org.mybatis.spring.SqlSessionFactoryBean”> <property name=“dataSource” ref=“db_user” /> </bean> <bean id=“ssf_account” class=“org.mybatis.spring.SqlSessionFactoryBean”>我爱线报网每日持续更新海量各大内部创业教程 <property name=“dataSource” ref=“db_account” /> </bean> <!–=============针对两个SqlSessionFactoryBean,各配置一个MapperScannerConfigurer============ –> <bean class=“org.mybatis.spring.mapper.MapperScannerConfigurer”> <property name=“sqlSessionFactoryBeanName” value=“ssf_user”/> <!–指定com.test.atom.mapper.ds_user包下的Use我爱线报网每日持续更新海量各大内部创业教程rMapper接口使用ssf_user获取底层数据库连接–> <property name=“basePackage” value=“com.test.atom.mapper.ds_user”/> </bean> <bean class=“org.mybatis.spring.mapper.MapperScannerConfigurer”> <property name=“sqlSessionFactoryBeanName” value=“ssf_account”/> <!–指定com.test.atom.mapper.ds_account包下的AccountMapper接口使用ssf_account获取底层我爱线报网每日持续更新海量各大内部创业教程数据库连接–> <property name=“basePackage” value=“com.test.atom.mapper.ds_account”/> </bean> <!–================配置atomikos事务管理器========================–> <bean id=“atomikosTransactionManager” class=“com.atomikos.icatch.jta.UserTransactionManager” init-method=“init” destroy-method=“close”> <property name=“forceShut我爱线报网每日持续更新海量各大内部创业教程down” value=“false”/> </bean> <!–============配置spring的JtaTransactionManager,底层委派给atomikos进行处理===============–> <bean id=“jtaTransactionManager” class=“org.springframework.transaction.jta.JtaTransactionManager”> <property name=“transactionManager” ref=“atomikosTransactionManager”/> </bean> <!–配置spring声明式事务管理我爱线报网每日持续更新海量各大内部创业教程器–> <tx:annotation-driven transaction-manager=“jtaTransactionManager”/> <bean id=“jtaService” class=“com.test.atom.service.JTAService”/> </beans>

测试代码

public class AtomikosSpringMybatisExample { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(“spring-atomi我爱线报网每日持续更新海量各大内部创业教程kos.xml”); JTAService jtaService = context.getBean(“jtaService”, JTAService.class); jtaService.insert(); } }

运行mian方法,代码执行后,db_user库的user表和db_account的account表中的确各插入了一条记录。

然后将JTAService中的异常模拟的注释打开,会发现出现异常后,两个库中都没有新插入的数据库,说明我们使用的JTA事务管理器的确保证数据的一致性了。

Atomikos配置

在掌握了Atomikos基本使用之后,我们对Atomiko我爱线报网每日持续更新海量各大内部创业教程s的配置进行一下简单的介绍。Atomikos在启动后,默认会从以下几个位置读取配置文件,这里笔者直接贴出atomikos源码进行说明:

com.atomikos.icatch.provider.imp.AssemblerImp#initializeProperties方法中定义了配置加载顺序逻辑:

@Override public ConfigProperties initializeProperties() { //读取classpath下的默认配置transactions-defaults.properties Properties defaults = newProperties(); loadP我爱线报网每日持续更新海量各大内部创业教程ropertiesFromClasspath(defaults, DEFAULT_PROPERTIES_FILE_NAME);//读取classpath下,transactions.properties配置,覆盖transactions-defaults.properties中相同key的值 Properties transactionsProperties = new Properties(defaults); loadPropertiesFromClasspath(transactionsProperties, TRANSACTIONS_PROPERTIES_FILE_NAME); //读我爱线报网每日持续更新海量各大内部创业教程取classpath下,jta.properties,覆盖transactions-defaults.properties、transactions.properties中相同key的值 Properties jtaProperties = new Properties(transactionsProperties); loadPropertiesFromClasspath(jtaProperties, JTA_PROPERTIES_FILE_NAME); //读取通过java -Dcom.atomikos.icatch.file方式指定的自定义配置文件路径,覆盖之前的同名配置Properti我爱线报网每日持续更新海量各大内部创业教程es customProperties =new Properties(jtaProperties); loadPropertiesFromCustomFilePath(customProperties); //最终构造一个ConfigProperties对象,来表示实际要使用的配置 Properties finalProperties = new Properties(customProperties); return new ConfigProperties(finalProperties); }

配置文件优先级:

transactions-defaults.properties<transa我爱线报网每日持续更新海量各大内部创业教程ctions.properties<jta.properties<自定义配置文件路径,后面的配置会覆盖之前同名key的配置。

其中

transactions-defaults.properties是atomikos自带的默认配置,位于transactions-xxx.jar中.

注意不同版本的默认配置可能不同。特别是3.x版本和4.x版本的差异比较明显。

以下是4.0.6中

transactions-default.properties中配置内容,笔者对这些配置进行了归类,如下: ===============================================================我爱线报网每日持续更新海量各大内部创业教程 ============ 事务管理器(TM)配置参数 ============== ===============================================================#指定是否启动磁盘日志,默认为true。在生产环境下一定要保证为true,否则数据的完整性无法保证 com.atomikos.icatch.enable_logging=true #JTA/XA资源是否应该自动注册 com.atomikos.icatch.automatic_resource_registration=true #JTA事务的默认超时时间,默认为10000msc我爱线报网每日持续更新海量各大内部创业教程om.atomikos.icatch.default_jta_timeout=10000#事务的最大超时时间,默认为300000ms。这表示事务超时时间由 UserTransaction.setTransactionTimeout()较大者决定。4.x版本之后,指定为0的话则表示不设置超时时间 com.atomikos.icatch.max_timeout=300000 #指定在两阶段提交时,是否使用不同的线程(意味着并行)。3.7版本之后默认为false,更早的版本默认为true。如果为false,则提交将按照事务中访问资源的顺序进行。com.atomikos.icatch.threaded_2我爱线报网每日持续更新海量各大内部创业教程pc=false #指定最多可以同时运行的事务数量,默认值为50,负数表示没有数量限制。在调用 UserTransaction.begin()方法时,可能会抛出一个”Max number of active transactions reached”异常信息,表示超出最大事务数限制 com.atomikos.icatch.max_actives=50 #是否支持subtransaction,默认为true com.atomikos.icatch.allow_subtransactions=true #指定在可能的情况下,否应该join 子事务(subtransactions),默认值为true。如果设置我爱线报网每日持续更新海量各大内部创业教程为false,对于有关联的不同subtransactions,不会调用XAResource.start(TM_JOIN) com.atomikos.icatch.serial_jta_transactions=true #指定JVM关闭时是否强制(force)关闭事务管理器,默认为false com.atomikos.icatch.force_shutdown_on_vm_exit=false #在正常关闭(no-force)的情况下,应该等待事务执行完成的时间,默认为Long.MAX_VALUEcom.atomikos.icatch.default_max_wait_time_on_shutdown我爱线报网每日持续更新海量各大内部创业教程=9223372036854775807 =============================================================== ========= 事务日志(Transaction logs)记录配置 ======= ===============================================================#事务日志目录,默认为./。 com.atomikos.icatch.log_base_dir=./ #事务日志文件前缀,默认为tmlog。事务日志存储在文件中,文件名包含一个数字后缀,日志文我爱线报网每日持续更新海量各大内部创业教程件以.log为扩展名,如tmlog1.log。遇到checkpoint时,新的事务日志文件会被创建,数字增加。 com.atomikos.icatch.log_base_name=tmlog #指定两次checkpoint的时间间隔,默认为500com.atomikos.icatch.checkpoint_interval=500 =============================================================== ========= 事务日志恢复(Recovery)配置 ============= ===============我爱线报网每日持续更新海量各大内部创业教程================================================#指定在多长时间后可以清空无法恢复的事务日志(orphaned),默认86400000ms com.atomikos.icatch.forget_orphaned_log_entries_delay=86400000 #指定两次恢复扫描之间的延迟时间。默认值为与com.atomikos.icatch.default_jta_timeout相同 com.atomikos.icatch.recovery_delay=${com.atomikos.icatch.default_jta_timeout} #提交失败我爱线报网每日持续更新海量各大内部创业教程时,再抛出一个异常之前,最多可以重试几次,默认值为5 com.atomikos.icatch.oltp_max_retries=5 #提交失败时,每次重试的时间间隔,默认10000mscom.atomikos.icatch.oltp_retry_interval=10000 =============================================================== ========= 其他 =============================== == =======================================我爱线报网每日持续更新海量各大内部创业教程======================== java.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory com.atomikos.icatch.client_demarcation=false java.naming.provider.url=rmi://localhost:1099 com.atomikos.icatch.rmi_export_class=none com.atomikos.icatch.trust_client_tm=false

打印日志

4.x版本我爱线报网每日持续更新海量各大内部创业教程之后,优先尝试使用slf4j,如果没有则尝试使用log4j,如果二者都没有,则使用JUL。

参见:

https://www.atomikos.com/Documentation/ConfiguringTheLogs

注意这里是说的是如何配置打印工作日志(work log),而前面说的是事务日志(transactions log),二者不是不同的。

本文源自:

http://www.tianshouzhi.com/api/tutorials/distributed_transaction/386

推荐阅读

给力项目线报网会员可免费下载 加入会员
友情提醒: 请尽量登录购买,防止付款了不发货!
QQ交流群:226333560 站长微信:qgzmt2
温馨提示:本站提供的一切软件、教程和内容信息都来自网络收集整理,仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负,版权争议与本站无关。用户必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序和内容,请支持正版,购买注册,得到更好的正版服务。我们非常重视版权问题,如有侵权请邮件与我们联系处理。敬请谅解!

给TA打赏
共{{data.count}}人
人已打赏
行业资讯

上虞驾校科目一模拟(浙江上虞:驾培驾考“一件事”改革,探索全流程“掌上办”模式)

2024-7-27 7:01:52

行业资讯

每日一词一句摘抄大全简单(每日一词“script”)

2024-7-27 7:22:12

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索