分佈式事務是指事務的參與者、支持事務的服務器、資源管理器以及事務管理器分別位於分佈系統的不同節點之上,在兩個或多個網絡計算機資源上訪問並且更新數據,將兩個或多個網絡計算機的數據進行的多次操作作爲一個整體進行處理。如不同銀行賬戶之間的轉賬。
對於在項目中接觸到JTA,大部分的原因是因爲在項目中需要操作多個數據庫,同時,可以保證操作的原子性,保證對多個數據庫的操作一致性。
1、項目結構圖:
2、pom.xml
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.nercita.ltxx</groupId>
- <artifactId>jtaTest</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <packaging>jar</packaging>
- <name>jtaTest</name>
- <url>http://maven.apache.org</url>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <spring.version>4.1.6.RELEASE</spring.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-test</artifactId>
- <version>${spring.version}</version>
- <scope>provided</scope>
- </dependency>
- <!-- 數據庫驅動-mysql -->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.26</version>
- </dependency>
- <!-- 數據庫驅動-oracle -->
- <dependency>
- <groupId>com.oracle</groupId>
- <artifactId>ojdbc6</artifactId>
- <version>12.1.0.1</version>
- </dependency>
- <dependency>
- <groupId>com.atomikos</groupId>
- <artifactId>transactions-jdbc</artifactId>
- <version>3.9.3</version>
- </dependency>
- <dependency>
- <groupId>javax.transaction</groupId>
- <artifactId>jta</artifactId>
- <version>1.1</version>
- </dependency>
- <!-- javaee-api包含了JavaEE規範中的api,如servlet-api,persistence-api, transaction-api等 -->
- <dependency>
- <groupId>javax</groupId>
- <artifactId>javaee-api</artifactId>
- <version>7.0</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-jdbc</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aspects</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <!-- aspectj weaver.jar 這是SpringAOP所要用到的依賴包 -->
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.8.2</version>
- </dependency>
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjrt</artifactId>
- <version>1.8.2</version>
- </dependency>
- </dependencies>
- </project>
2、applicationContext.xml
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:jee="http://www.springframework.org/schema/jee"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
- <!-- 引入屬性文件 -->
- <context:property-placeholder location="classpath*:/application.properties" />
- <!-- 對於沒有被IOC容器管理的對象也可以使用依賴注入 -->
- <context:spring-configured />
- <!-- 註解bean及依賴注入 -->
- <context:component-scan base-package="org.nercita.ltxx.jtatest" />
- <!-- mysql數據源1 -->
- <bean id="masterDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
- init-method="init" destroy-method="close">
- <description>master xa datasource</description>
- <property name="uniqueResourceName">
- <value>masterDataSource</value>
- </property>
- <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
- <property name="xaProperties">
- <props>
- <prop key="user">root</prop>
- <prop key="password">0729</prop>
- <prop key="URL">jdbc:mysql://127.0.0.1:3306/master</prop>
- </props>
- </property>
- <property name="poolSize" value="10"/>
- </bean>
- <!-- mysql數據源1 -->
- <bean id="slaveDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
- init-method="init" destroy-method="close">
- <description>slave xa datasource</description>
- <property name="uniqueResourceName">
- <value>slaveDataSource</value>
- </property>
- <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
- <property name="xaProperties">
- <props>
- <prop key="user">root</prop>
- <prop key="password">0729</prop>
- <prop key="URL">jdbc:mysql://127.0.0.1:3306/slave</prop>
- </props>
- </property>
- <property name="poolSize" value="10"/>
- </bean>
- <!-- atomikos事務管理器 -->
- <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
- init-method="init" destroy-method="close">
- <description>UserTransactionManager</description>
- <property name="forceShutdown">
- <value>true</value>
- </property>
- </bean>
- <!-- atomikos用戶事務實現 -->
- <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
- <property name="transactionTimeout" value="300" />
- </bean>
- <!-- spring 事務管理器 -->
- <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
- <!--注入 atomikos事務管理器 -->
- <property name="transactionManager">
- <ref bean="atomikosTransactionManager" />
- </property>
- <!--注入 atomikos用戶事務實現 -->
- <property name="userTransaction">
- <ref bean="atomikosUserTransaction" />
- </property>
- </bean>
- <!-- spring事務模板 -->
- <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
- <property name="transactionManager">
- <ref bean="springTransactionManager" />
- </property>
- </bean>
- <!--jdbc模板 -->
- <bean id="masterJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <constructor-arg>
- <ref bean="masterDataSource" />
- </constructor-arg>
- </bean>
- <!--jdbc模板 -->
- <bean id="slaveJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <constructor-arg>
- <ref bean="slaveDataSource" />
- </constructor-arg>
- </bean>
- <!-- 開啓Spring註解式事務 -->
- <tx:annotation-driven transaction-manager="springTransactionManager"/>
- </beans>
ps:數據庫和表需自行創建
3、java類
dao:
- package org.nercita.ltxx.jtatest.dao;
- import javax.annotation.Resource;
- import org.springframework.jdbc.core.JdbcTemplate;
- import org.springframework.stereotype.Repository;
- @Repository
- public class JtaTestMasterDao{
- @Resource(name="masterJdbcTemplate")
- JdbcTemplate masterJdbcTemplate;
- public String master() {
- masterJdbcTemplate.execute("update teacher set name='master' where id=1");
- return "success";
- }
- public void update() {
- masterJdbcTemplate.execute("update teacher set name='8' where id=1");
- System.out.println("update");
- masterJdbcTemplate.execute("fff teacher set name=''6' where id=1");
- }
- }
- package org.nercita.ltxx.jtatest.dao;
- import javax.annotation.Resource;
- import org.springframework.jdbc.core.JdbcTemplate;
- import org.springframework.stereotype.Repository;
- @Repository
- public class JtaTestSlaveDao{
- @Resource(name="slaveJdbcTemplate")
- JdbcTemplate slaveJdbcTemplate;
- public String slave() {
- slaveJdbcTemplate.execute("update student set name='slave' where id=1");
- return "success";
- }
- }
service:
- package org.nercita.ltxx.jtatest.service;
- import javax.annotation.Resource;
- import javax.transaction.SystemException;
- import javax.transaction.UserTransaction;
- import org.nercita.ltxx.jtatest.dao.JtaTestMasterDao;
- import org.nercita.ltxx.jtatest.dao.JtaTestSlaveDao;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.TransactionStatus;
- import org.springframework.transaction.annotation.Transactional;
- import org.springframework.transaction.jta.JtaTransactionManager;
- import org.springframework.transaction.support.TransactionCallback;
- import org.springframework.transaction.support.TransactionCallbackWithoutResult;
- import org.springframework.transaction.support.TransactionTemplate;
- @Service
- public class JtaTestService{
- @Resource(name = "springTransactionManager")
- private JtaTransactionManager txManager;
- @Autowired
- private JtaTestMasterDao jtaTestMasterDao;
- @Autowired
- private JtaTestSlaveDao jtaTestSlaveDao;
- @Resource(name = "transactionTemplate")
- private TransactionTemplate transactionTemplate;
- //編程式
- public String test() {
- UserTransaction userTx = txManager.getUserTransaction();
- try {
- userTx.begin();
- jtaTestMasterDao.master();
- jtaTestSlaveDao.slave();
- int a=1/0;
- System.out.println(a);
- userTx.commit();
- } catch (Exception e) {
- System.out.println("捕獲到異常,進行回滾" + e.getMessage());
- e.printStackTrace();
- try {
- userTx.rollback();
- } catch (IllegalStateException e1) {
- System.out.println("IllegalStateException:" + e1.getMessage());
- } catch (SecurityException e1) {
- System.out.println("SecurityException:" + e1.getMessage());
- } catch (SystemException e1) {
- System.out.println("SystemException:" + e1.getMessage());
- }
- }
- return null;
- }
- //聲明式
- @Transactional
- public void update(){
- jtaTestMasterDao.master();
- int a=1/0;
- System.out.println(a);
- jtaTestSlaveDao.slave();
- }
- //事務模板方式
- public void test3() {
- transactionTemplate.execute(new TransactionCallbackWithoutResult(){
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- try {
- jtaTestMasterDao.master();
- jtaTestSlaveDao.slave();
- int a=1/0;
- System.out.println(a);
- } catch (Exception ex) {
- // 通過調用 TransactionStatus 對象的 setRollbackOnly() 方法來回滾事務。
- status.setRollbackOnly();
- ex.printStackTrace();
- }
- }
- });
- /*
- //有返回值的回調
- Object obj=transactionTemplate.execute(new TransactionCallback(){
- @Override
- public Object doInTransaction(TransactionStatus status) {
- return 1;
- }
- });
- */
- }
- }
controller(本文直接使用controller進行測試)
- package org.nercita.ltxx.jtatest.web;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.nercita.ltxx.jtatest.service.JtaTestService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = "classpath:applicationContext.xml")
- @Controller
- public class JtaTestController {
- @Autowired
- private JtaTestService jtaTestService;
- //MySQL的數據庫引擎必須是InnoDB,否則無法回滾
- @Test
- public void test(){
- jtaTestService.test();
- }
- @Test
- public void test2(){
- jtaTestService.update();
- }
- @Test
- public void test3(){
- jtaTestService.test3();
- }
- }
4、數據源詳細參數配置
- <!-- 兩個數據源的功用配置,方便下面直接引用 -->
- <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
- destroy-method="close" abstract="true">
- <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
- <property name="poolSize" value="10" />
- <property name="minPoolSize" value="10"/>
- <property name="maxPoolSize" value="30"/>
- <property name="borrowConnectionTimeout" value="60"/> <!--獲取連接失敗重新獲等待最大時間,在這個時間內如果有可用連接,將返回-->
- <property name="reapTimeout" value="20"/> <!--最大獲取數據時間,如果不設置這個值,Atomikos使用默認的5分鐘,那麼在處理大批量數據讀取的時候,一旦超過5分鐘,就會拋出類似 Resultset is close 的錯誤.-->
- <property name="maxIdleTime" value="60"/> <!--最大閒置時間,超過最小連接池連接的連接將將關閉-->
- <property name="maintenanceInterval" value="60" /> <!--連接回收時間-->
- <property name="loginTimeout" value="60" /> <!--java數據庫連接池,最大可等待獲取datasouce的時間-->
- <property name="logWriter" value="60"/>
- <property name="testQuery">
- <value>select 1</value>
- </property>
- </bean>
- <!-- 配置第一個數據源 -->
- <bean id="dataSource" parent="abstractXADataSource">
- <!-- value只要兩個數據源不同就行,隨便取名 -->
- <property name="uniqueResourceName" value="mysql/sitestone" />
- <property name="xaDataSourceClassName"
- value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
- <property name="xaProperties">
- <props>
- <prop key="URL">${jdbc.url}</prop>
- <prop key="user">${jdbc.username}</prop>
- <prop key="password">${jdbc.password}</prop>
- </props>
- </property>
- </bean>
- <!-- 配置第二個數據源-->
- <bean id="dataSourceB" parent="abstractXADataSource">
- <!-- value只要兩個數據源不同就行,隨便取名 -->
- <property name="uniqueResourceName" value="mysql/sitesttwo" />
- <property name="xaDataSourceClassName"
- value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
- <property name="xaProperties">
- <props>
- <prop key="URL">${jdbca.url}</prop>
- <prop key="user">${jdbca.username}</prop>
- <prop key="password">${jdbca.password}</prop>
- </props>
- </property>
- </bean>