背景描述:随着应用系统复杂度越来越复杂,之前在一个库中完成所有业务操作已不满足现状业务系统的要求,数据流转往往存在于多个库中进行,全局事务是指在一个事务中涉及到几个事务参入者,这些事务参入者可以是我们常见的数据库操作,消息(MQ)操作等等.如同时进行下面的操作,比如"转账"操作发生在两个数据库:
1,从数据库A的的表中将某个帐号的余额减少.
2从数据库B的的表中将某个帐号的余额增加.
3,提交在数据库A中的操作.
4,提交在数据库B中的操作.
通常单个数据库只能保证本数据库的事务要么提交要么回滚,当涉及的事务跨数据库的时候,就需要一个在两个数据库之间进行事务"协调"的事务管理器了,而数据库也需要为参入这种"协调"处理实现特殊协议,叫做XA或者两阶段提交协议.Java对这种全局事务管理是通过JTA规范来做的,通常就是begin,commit或者rollback的形式,本文通过示例简单介绍一下Spring利用Weblogic提供的JTA事务管理器管理全局事务.
*注意,如果用Spring的事务声明,WebLogicJtaTransactionManager只能管理在Weblogic通过DataSource配置的数据库操作,或者在Weblogic上配置的JMS
具体操作:
第一步:在Weblogic上配置连接到两个不同数据库的DataSource,需要DataSource支持XA.
第二步:在spring-context中配置如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">
<bean id="db1jdbcDAO" class="com.test.spring.tx.xawl.dao.DB1jdbcDAO">
<property name="dataSource" ref="dataSource1" />
</bean>
<bean id="db2jdbcDAO" class="com.test.spring.tx.xawl.dao.DB2jdbcDAO">
<property name="dataSource" ref="dataSource2" />
</bean>
<bean id="dataSource1"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>DataSource-0</value>
</property>
</bean>
<bean id="dataSource2"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>DataSource-1</value>
</property>
</bean>
<bean id="buzSingleService" class="com.test.spring.tx.xawl.Service.BuzSingleService">
<property name="db1" ref="db1jdbcDAO" />
<property name="db2" ref="db2jdbcDAO" />
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager" >
<property name="transactionManagerName" value="javax.transaction.TransactionManager" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- <context:component-scan base-package="com.test.spring.tx.xawl.controller" /> -->
<context:component-scan base-package="com.test">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Service" />
</context:component-scan>
</beans>
3,Java Dao代码:
package com.test.spring.tx.xawl.dao;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import com.test.spring.tx.xawl.Service.BuzSingleService;
public class DB1jdbcDAO {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void testInsert(int id, String val) {
this.jdbcTemplate.update("insert into TEST1 (ID, NAME) values (?, ?)", id, val);
}
}
package com.test.spring.tx.xawl.dao;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
//import weblogic.jdbc.jta.DataSource;
public class DB2jdbcDAO {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void testInsert(int id, String val) {
this.jdbcTemplate.update("insert into TEST (ID, NAME) values (?, ?)", id, val);
}
}
4,service代码
package com.test.spring.tx.xawl.Service;
import org.springframework.transaction.annotation.Transactional;
import com.test.spring.tx.xawl.dao.DB1jdbcDAO;
import com.test.spring.tx.xawl.dao.DB2jdbcDAO;
public class BuzSingleService {
private DB1jdbcDAO db1;
private DB2jdbcDAO db2;
@Transactional(value="transactionManager",rollbackFor=Exception.class)
public void testTX1() throws Exception {
db1.testInsert(0, "db1jdbcDAO val0");
db2.testInsert(0, "db2jdbcDAO val0");
}
public DB1jdbcDAO getDb1() {
return db1;
}
public void setDb1(DB1jdbcDAO db1) {
this.db1 = db1;
}
public DB2jdbcDAO getDb2() {
return db2;
}
public void setDb2(DB2jdbcDAO db2) {
this.db2 = db2;
}
}
5,controller层:
package com.test.spring.tx.xawl.controller;
import java.util.Date;
import javax.jms.Destination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.inspur.ahDqyJMS.service.ProducerServiceImpl;
import com.test.spring.tx.xawl.Service.BuzSingleService;
@Controller
@RequestMapping("testJTA")
public class TestControllerJTA {
@Autowired
private BuzSingleService buzSingleService;
@RequestMapping("first")
public void first() {
try {
buzSingleService.testTX1();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
6,在浏览器输入http://localhost:7001/SpringJMS/testJTA/first.do在两个数据库中正常插入数据.,如果将testTX1改为如下,两个数据库中都不会有数据插入.
public void testTX1() throws Exception {
db1.testInsert(0, "db1jdbcDAO val0");
db2.testInsert(0, "db2jdbcDAO val0");
String nullStr = null;
nullStr.length();
}
遇到的问题及解决方案:
问题一:异常描述:weblogic.transaction.TimedOutException: Transaction timed out after 40 seconds
原因分析:超时秒数: 指定在两阶段提交事务处理中允许活动事务处理处于第一阶段的最长时间 (秒)。如果指定的时间到期, 则将自动回退该事务处理。默认的是30秒
解决方法:
登陆AdminConsole
Environments --> <DomainName> (ClickHere) ---> Configuration (Tab) ---> JTA (SubTab) ----> in this page try to set the Transcation timeout
将该值设大一点,我设成了600秒,一般不会超过这么多了,超过的话说明代码需要优化。
设置完 保存成功后 不需要重启 就会生效。