本文涉及的代碼完整例子下載http://download.csdn.net/download/lanseba/2508881
有的時候我們的程式不止一個資料來源,並且需要在多資料來源中保證事務,那麼就需要用到分散式交易了..JTA -- java transaction api 。。哥研究了了一下。做了下總結,並給各位掉面人分享下。以便就分散式交易這個問題一勞永逸.
環境 : mysql5.1 jdk 1.5+ spring 2.5 hibernate 3.x Spring 對jta的支援真的很好。。。
分散式交易必須使用的資料庫支援 並且提供 XA 串連驅動 如 mysql 的5.0+版本驅動中就有 com.mysql.jdbc.jdbc2.optional.MysqlXAConnection 等 這些對 XA 分布事務支援的串連 ,所以理所當然我們要使用XA事務就必須使用這種XA串連
當然上面提到的 mysql5.1 是支援分布事務的 ,mysql驅動需要5.0+ 哥推薦 mysql-connector-java-5.1.0
幾個驅動比較下來 哥覺得這個驅動最穩定 對XA的支援最好
小二 上代碼 上配置!
資料庫1 :jtatest1
表:tusers
列 類型
id int 主鍵自增
name varchar(50) 非空
--------------------------------------------------------
CREATE TABLE `tusers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
資料庫1 :jtatest2
表:product
列 類型
id int 主鍵自增
price float(11,0) 非空
--------------------------------------------------------
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`price` float(11,0) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
實體類,以及hibernate orm對應檔
package jtatest1;
public class Users implements java.io.Serializable {
private Integer id;
private String name;
public Users() {
}
public Users(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="jtatest1.Users" table="tusers" >
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="java.lang.String">
<column name="name" length="100" not-null="true" />
</property>
</class>
</hibernate-mapping>
package jtatest2;
public class Product implements java.io.Serializable {
private Integer id;
private Float price;
public Product() {
}
public Product(Integer id, Float price) {
this.id = id;
this.price = price;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public Float getPrice() {
return this.price;
}
public void setPrice(Float price) {
this.price = price;
}
}
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="jtatest2.Product" table="product" >
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="price" type="java.lang.Float">
<column name="price" precision="53" scale="0" not-null="true" />
</property>
</class>
</hibernate-mapping>
下面是DAO 和 BIZ
package jtatest.daoimpl;
import jtatest.dao.ProductDao;
import jtatest2.Product;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class ProductDaoimpl extends HibernateDaoSupport implements ProductDao {
public void save(Product p){
this.getHibernateTemplate().save(p);
}
}
package jtatest.daoimpl;
import jtatest.dao.UserDao;
import jtatest1.Users;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class UserDaoimpl extends HibernateDaoSupport implements UserDao {
public void save(Users u){
this.getHibernateTemplate().save(u);
}
}
package jtatest.dao;
import jtatest2.Product;
public interface ProductDao {
public abstract void save(Product p);
}
package jtatest.dao;
import jtatest1.Users;
public interface UserDao {
public abstract void save(Users u);
}
package jtatest.bizimpl;
import jtatest.biz.Biz;
import jtatest.dao.ProductDao;
import jtatest.dao.UserDao;
import jtatest1.Users;
import jtatest2.Product;
public class Bizimpl implements Biz {
private ProductDao productDao = null;
private UserDao userDao = null;
public void save(Product p, Users u) {
productDao.save(p);
userDao.save(u);
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
package jtatest.biz;
import jtatest1.Users;
import jtatest2.Product;
public interface Biz {
public void save(Product p, Users u);
}
2個Hibernate 的配置
hibernate-jtatest1.cfg.xml
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<mapping resource="jtatest1/Users.hbm.xml" />
</session-factory>
</hibernate-configuration>
-------------------------------------------
hibernate-jtatest2.cfg.xml
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<mapping resource="jtatest2/Product.hbm.xml" />
</session-factory>
</hibernate-configuration>
好了 基本垃圾代碼就是上面了的了 剩下的就是使用spring管理JTA了 可以使用 JOTM 或者是 ATOMIKOS 等 但是 貌似JOTM需要 JNDI 的支援 脫離不了Web伺服器,
而 atomikos 則不需要JNDI的支援 所以哥推薦使用 atomikos 它也是開源產品, http://www.atomikos.com/downloads/transactions-essentials/3.5.5/AtomikosTransactionsEssentials-3.5.5.zip
使用spring 管理jta事務 你不需要寫一句事務代碼 真是NB啊。。。
各位掉面人可能主要道上面的hibernate配置中沒有使用者名稱密碼,那是因為我們需要在spring中注入一個XADataSource到hibernate中去 好讓hibernate擷取的串連是XAConnection
這裡使用atomikos的 XADataSource 配置 :
<bean id="ds1" class="com.atomikos.jdbc.SimpleDataSourceBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/ds1</value> 這裡注意 多個datasource中不能重名
</property>
<property name="xaDataSourceClassName">
<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
</property>
<property name="xaDataSourceProperties">
<value>URL=jdbc:mysql://localhost:3306/jtatest1?user=root;password=admin</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>
<bean id="ds2"
class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init"
destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/ds2</value>
</property>
<property name="xaDataSourceClassName">
<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
</property>
<property name="xaDataSourceProperties">
<value>URL=jdbc:mysql://localhost:3306/jtatest2?user=root;password=admin</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>
這樣就可以給hibernate 注入 datasource了
<bean id="sessionFactory1"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation"
value="classpath:hibernate-jtatest1.cfg.xml" />
<property name="dataSource" ref="ds1"></property>
</bean>
<bean id="sessionFactory2"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation"
value="classpath:hibernate-jtatest2.cfg.xml" />
<property name="dataSource" ref="ds2"></property>
</bean>
下面管理 BIZ 和DAO
<bean id="biz" class="jtatest.bizimpl.Bizimpl">
<property name="productDao" ref="productDao" />
<property name="userDao" ref="userDao" />
</bean>
<bean id="userDao" class="jtatest.daoimpl.UserDaoimpl">
<property name="sessionFactory" ref="sessionFactory1" />
</bean>
<bean id="productDao" class="jtatest.daoimpl.ProductDaoimpl">
<property name="sessionFactory" ref="sessionFactory2" />
</bean>
OK 萬事俱備了 就等陪事務了 基本和spring 管理的普通事務過程差不多
首先需要一個交易管理員
<bean id="atomikosTransactionManager"
class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown">
<value>true</value>
</property>
</bean>
com.atomikos.icatch.jta.UserTransactionManager 實現了 javax.transaction.TransactionManager 介面 這個介面不能和 tx:advice 的transaction-manager相容
,so 還需封裝下
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction" ref="atomikosUserTransaction" />
<property name="transactionManager" ref="atomikosTransactionManager"></property>
</bean>
這個是atomikos事務的實現
<bean id="atomikosUserTransaction"
class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout">
<value>6000</value>
</property>
</bean>
這樣 配的txManager就穿上了Spring交易管理員的外套了
下面就是 織入通知了
<tx:advice id="advice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="*" read-only="false" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="point"
expression=" execution(* jtatest.bizimpl.*.*(..))" />
<aop:advisor advice-ref="advice" pointcut-ref="point" />
</aop:config>
大功告成 可以測試了
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
BeanFactory bf = context;
Biz biz = (Biz) bf.getBean("biz");
Product p = new Product();
p.setPrice(10f);
Users u = new Users();
u.setName(null); //非空欄位 插入一個空試試 ,再給個值試試
biz.save(p, u);
}