您的位置:首页 > 运维架构 > Tomcat

Java事务--spring+atomikos+tomcat实现分布式事务

2016-07-22 11:46 841 查看
        JTA是J2EE的规范之一,如果使用JTA,我们需要去实现相应接口。tomcat是web服务器,没有实现J2EE规范,如果我们需要在tomcat服务器使用JTA,需要自己去实现JTA的接口。本文介绍如何使用tomcat+JTA实现多数据源的分布式事务。
        一 选型

tomcat需要使用插件实现JTA,常用插件有jotm和atomikos,本文以atomikos为例。jar如下:

atomikos-util-1.0.jar
cglib-nodep-2.2.2.jar
transactions-3.7.0.jar
transactions-api-3.7.0.jar
transactions-jdbc-3.7.0.jar
transactions-jta-3.7.0.jar

如果是maven项目,POM配置如下:

<span style="white-space:pre"> </span><dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jdbc</artifactId>
<version>3.9.1</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.4</version>
</dependency>

        二 配置数据库

        我们使用了两个数据库做测试,模拟多数据源的业务场景。数据库如下:



        两个数据库分别是log和user,每个数据库中创建一张表,分别是log和user,数据库类型都是MySQL(两个数据库类型不一致也可以)。
        三 持久层

        我们直接使用JdbcTemplate实现持久化,代码如下:

<span style="font-size:18px;">public class LogDao extends JdbcDaoSupport {
public boolean insertLog(String id, String content) {
JdbcTemplate template = getJdbcTemplate();
template.execute("insert into log values('" + id + "','" + content + "')");
return true;
}
}</span>
<span style="font-size:18px;">public class UserDao extends JdbcDaoSupport {
public boolean insertUser(String id, String name) {
JdbcTemplate template = getJdbcTemplate();
template.execute("insert into user values('" + id + "','" + name + "')");
return true;
}
}</span>

        持久层创建两个类,每个类添加一个方法,向数据库插入一条记录。
        四 service层

        我们要把事务加到这一层,代码如下:

@Transactional
public class UserService {
private UserDao userDao;
private LogDao logDao;

public void saveUser(String id, String name) {
boolean flag1 = userDao.insertUser(id, name);
System.out.println("zhang san save success!");
int i = 1 / 0; // 制造异常
boolean flag2 = logDao.insertLog(id, id + "_" + name);
if(flag1 && flag2){
System.out.println("保存成功");
}
/*boolean flag2 = userDao.insertUser("2","lisi");
System.out.println("li si save success!");*/
}

public UserDao getUserDao() {
return userDao;
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

public LogDao getLogDao() {
return logDao;
}

public void setLogDao(LogDao logDao) {
this.logDao = logDao;
}
}
        分别调用持久层方法,并且在两次调用中间抛出异常,看数据是否能持久化到数据库。
        五 配置文件

        1 资源管理器配置

<!-- 数据库1 -->
<bean id="db1" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/main</value>
</property>
<property name="xaDataSourceClassName">
<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
</property>
<property name="xaDataSourceProperties">
<value>URL=jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf-8;user=root;password=123456</value>
</property>
<property name="exclusiveConnectionMode">
<value>true</value>
</property>
<property name="connectionPoolSize">
<value>3</value>
</property>
<property name="validatingQuery">
<value>SELECT 1</value>
</property>
</bean>

<!-- 数据库2 -->
<bean id="db2" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/secondary</value>
</property>
<property name="xaDataSourceClassName">
<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
</property>
<property name="xaDataSourceProperties">
<value>URL=jdbc:mysql://localhost:3306/log?useUnicode=true&characterEncoding=utf-8;user=root;password=123456</value>
</property>
<property name="exclusiveConnectionMode">
<value>true</value>
</property>
<property name="connectionPoolSize">
<value>3</value>
</property>
<property name="validatingQuery">
<value>SELECT 1</value>
</property>
</bean>
        2 持久层注入

<bean id="userDao" class="com.zdp.dao.UserDao">
<property name="dataSource" ref="db1" />
</bean>

<bean id="logDao" class="com.zdp.dao.LogDao">
<property name="dataSource" ref="db2" />
</bean>

<bean id="userService" class="com.zdp.service.UserService">
<property name="userDao" ref="userDao" />
<property name="logDao" ref="logDao" />
</bean>
        3 事务管理器配置

<bean id="userTransactionManager" init-method="init" destroy-method="close"
class="com.atomikos.icatch.jta.UserTransactionManager">
<property name="forceShutdown" value="true" />
</bean>

<bean id="userTransactionImp" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300"/>
</bean>

<bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="userTransactionManager" />
<property name="userTransaction" ref="userTransactionImp" />
<property name="allowCustomIsolationLevels" value="true"/>
</bean>
        4 使用AOP管理事务

<tx:advice id="txAdvice" transaction-manager="jtaTransactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="create*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="del*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>

<!-- 事务管理 -->
<aop:config proxy-target-class="true">
<aop:advisor pointcut="execution(* com.zdp.service..*.*(..))" advice-ref="txAdvice"/>
</aop:config>
        六 测试类

public class UserTest {
@Test
public void testSave() {
ApplicationContext cxt = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserService us = (UserService) cxt.getBean("userService");
us.saveUser("1", "zhangsan");
}
}

        七 总结

        经过测试,速度比较慢,如果系统对性能要求较高,不建议使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JTA atomikos