本文配项目源码,下载地址为:springhibernateatomikos.zip
附官方提供Atomikos与tomcat的整合:http://www.atomikos.com/Documentation/Tomcat7Integration35
事务管理器的选择
Spring重要的一个优点就是可以帮我们管理事务。Spring可以托管的事务分两类,
本地数据库事务;
JTA事务。
本地事务只能管理一个数据源,不能跨数据源。如果想跨数据源,则必须使用JTA事务。
若想使用JTA服务,只有两种选择:
J2EE容器。所有的J2EE容器都支持JTA规范。
独立于容器的第三方软件。比如JTOM, Atomikos, JbossTS等。独立JTS可以脱离容器运行。
因为tomcat不是一个完整的J2EE容器,不支持JTA,所以必须结合第三方软件实现JTA。
源码结构
App.java为程序的入口。里面提供了两个测试方法,一个运行本地事务,一个运行JTA事务。
dao和hb与模型不多说。
JtaService运行的是JTA事务,里面会对两个数据源进行操作。
Service运行的是本地事务。
applicationContext.xml定义了本地事务下的容器。
applicationContext.xml定义了JTA事务下的容器,它依赖ds1.xml和ds2.xml。ds1定义了数据源A,ds2定义了数据源B。
ds1.sql创建了数据库A,ds2.sql创建了数据库B。
开发过程
创建Maven项目
创建maven项目,然后编辑pom.xml,引入我们需要的资源:
其中c3p0只是为本地事务服务的。如果用JTA事务,则必须配置XADatasourse。XADatasource由J2EE容器提供,或者向本项目,由Atomikos提供。
除了最后一个资源,其它资源都一目了然,很容易找到。最后一个资源是由com.automikos提供的:
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-hibernate3</artifactId>
<version>3.9.3</version>
</dependency>
开发本地事务程序部分(略)
请直接查看源码。
编写JtaService
public class JtaService {
private CustomerDao customerDaoA;
private CustomerDao customerDaoB;
@Transactional(propagation=Propagation.REQUIRED, readOnly=false, isolation=Isolation.READ_COMMITTED)
public void createCustomer(Customer c, Customer c2) {
this.customerDaoA.saveCustomer(c);
this.customerDaoB.saveCustomer(c2);
}
// getters and setters
}
在JtaService中,使用来自两个数据源的customerDao。虽然两个数据源的数据库schema完全一样,但它们的确是跨数据库。customerDaoA和customerDaoB将在IOC容器中装配。
定义数据源A
<!-- ds1.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:s="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<bean id="dataSource1"
class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName"><value>dataSource1</value></property>
<property name="xaDataSourceClassName">
<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
</property>
<property name="xaProperties">
<props>
<prop key="user">mysql</prop>
<prop key="password">mysql</prop>
<prop key="URL">jdbc:mysql://localhost:3306/test1</prop>
</props>
</property>
<property name="poolSize" value="3"/>
</bean>
<bean id="sessionFactory1"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" destroy-method="destroy">
<property name="dataSource" ref="dataSource1" />
<property name="mappingResources">
<list>
<value>com/xpbug/hb/Customer.hbm.xml</value>
<value>com/xpbug/hb/Order.hbm.xml</value>
<value>com/xpbug/hb/Item.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.format_sql=false
</value>
</property>
</bean>
<bean id="customerDaoA" class="com.xpbug.dao.CustomerDao">
<property name="sessionFactory" ref="sessionFactory1"/>
</bean>
</beans>
定义数据源B
略,与A类似,只是不同的名称和指向不同的DB。
定义TransactionManager
<!-- applicationContext-jta.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:s="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<import resource="ds1.xml" />
<import resource="ds2.xml" />
<bean id="service" class="com.xpbug.service.JtaService">
<property name="customerDaoA" ref="customerDaoA"></property>
<property name="customerDaoB" ref="customerDaoB"></property>
</bean>
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown" value="false" />
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.J2eeUserTransaction">
<property name="transactionTimeout" value="300" />
</bean>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"
depends-on="atomikosTransactionManager,atomikosUserTransaction">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
至此,装配完成。
Eclipse中本地运行
public static void main( String[] args ) {
//new App().testLocalTransaction();
new App().testJtaTransaction();
}
private void testJtaTransaction() {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-jta.xml");
JtaService s = (JtaService) context.getBean("service");
Customer customer = new Customer();
customer.setName("A");
Order order = new Order();
order.setName("o1");
order.setCustomer(customer);
customer.getOrders().add(order);
Customer c1 = customer;
customer = new Customer();
customer.setName("B");
order = new Order();
order.setName("o2");
order.setCustomer(customer);
customer.getOrders().add(order);
Customer c2 = customer;
s.createCustomer(c1, c2);
context.close();
}
查看两个数据库A和B是否插入了数据。
为了测试原子性,修改JtaService,人为破坏程序执行:
@Transactional(propagation=Propagation.REQUIRED, readOnly=false, isolation=Isolation.READ_COMMITTED)
public void createCustomer(Customer c, Customer c2) {
this.customerDaoA.saveCustomer(c);
this.customerDaoB.saveCustomer(c2);
throw new RuntimeException();
}
让方法createCustomer最后抛出异常。运行App,查看两个数据库是否插入数据。