Spring3.0+Hibernate+Atomikos多数据源分布式事务管理

来源:互联网 发布:金融大数据网站有哪些 编辑:程序博客网 时间:2024/06/05 20:39

一、概念

分布式事务
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简言之,同时操作多个数据库保持事务的统一,达到跨库事务的效果。

JTA
JTA,即Java Transaction API,JTA允许应用程序执行分布式事务处理———在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。

JTA和JTS

Java事务API(JTA:Java Transaction API)和它的同胞Java事务服务(JTS:Java Transaction Service),为J2EE平台提供了分布式事务服务(distributed transaction)。
一个分布式事务(distributed transaction)包括一个事务管理器(transaction manager)和一个或多个资源管理器(resource manager)。
一个资源管理器(resource manager)是任意类型的持久化数据存储。
事务管理器(transaction manager)承担着所有事务参与单元者的相互通讯的责任。

JTA与JDBC

JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中:JDBC连接、JDO PersistenceManager 对象、JMS 队列、JMS 主题、企业JavaBeans(EJB)、一个用J2EE Connector Architecture 规范编译的资源分配器。

JOTM
JOTM (Java Open Transaction Manager)是一个开源独立的事务管理器,支持分布式事务,Spring3.X已经不再支持JOTM

Atomikos
Atomikos TransactionsEssentials是一个为Java平台提供增值服务的并且开源类事务管理器,支持分布式事务,本文所采用的技术。

Atomikos是目前在分布式事务管理中做得相当不错的开源软件。有10年以上的经验,Atomikos保障您的关键事务和防止昂贵的数据丢失在发生系统故障或事故中.Atomikos支持XA(全局事务)和NON-XA(非全局事务),NON-XA效率高于XA。本文主要是讲XA事件,因为要在不同的数据库中操作多张表。

接下来说一下Spring.x+Hibernate中集成Atomikos构建JTA的分布式事务

二、Spring.x+Hibernate+Atomikos集成
Spring.x+Hibernate怎么集成此处省略不说,主要讲解Spring.x+Atomikos的集成,操作步骤如下:

1、下载Atomikos,需要以下这些包

atomikos-util-1.0.jarcglib-nodep-2.2.2.jartransactions-3.7.0.jartransactions-api-3.7.0.jartransactions-jdbc-3.7.0.jartransactions-jta-3.7.0.jar

2、配置 
在applicationContext.xml文件当中作如下配置

<?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:aop="http://www.springframework.org/schema/aop" 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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/flex http://www.springframework.org/schema/flex/spring-flex-1.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
default-autowire="byName">


<!-- 使Spring关注Annotation -->
<context:annotation-config />

<!-- 读取参数配置文件信息 -->
<!-- <context:property-placeholder location="classpath*:jta.properties"/> -->

<bean id="BaseService" class="com.test.service.impl.BaseServiceImpl" />


<!-- 让Spring通过自动扫描来查询和管理Bean -->
<context:component-scan base-package="com.test">
<context:include-filter type="regex" expression=".dao.impl.*"/>
<context:include-filter type="regex" expression=".serviec.impl.*"/>
<context:include-filter type="regex" expression=".entity.*"/>
</context:component-scan>

<!-- 数据库tms -->
<bean class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" 
id="tmsDataSource">
<!-- Set unique name for this DataSource -->  
        <property name="uniqueResourceName"><value>mysql/tms</value></property>
        <!-- Set XADatasource class name-->  
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="user">root</prop>
                <prop key="password">root</prop>
                <prop key="url">jdbc:mysql://localhost:3306/tms</prop>
            </props>
        </property>
        <!-- set properties for datasource connection pool -->  
        <property name="poolSize" value="3" />
        <!-- 管理 Connection 被占用的时间 -->
        <!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误 -->
        <property name="reapTimeout"><value>20000</value></property>  
</bean>


<!-- 数据库his -->
<bean class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"
id="hisDataSource">
<property name="uniqueResourceName"><value>mysql/his</value></property>
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="user">root</prop>
                <prop key="password">root</prop>
                <prop key="url">jdbc:mysql://localhost:3306/his</prop>
            </props>
        </property>
        <!-- 连接池里面连接的个数 -->
        <property name="poolSize" value="3" />
        <!-- 管理 Connection 被占用的时间 -->
        <!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误 -->
        <property name="reapTimeout"><value>20000</value></property>  
</bean>


<!-- sessionFactory的配置 -->


<!-- 将hibernate数据源注入sessionFactory -->
    <bean id="tmsSessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="tmsDataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.test.entity</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <!-- 慎重(推荐禁止) 固定为update -->
                <!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
            </props>
        </property>
    </bean>
    
    <!-- 将hibernate数据源注入sessionFactory -->
    <bean id="hisSessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="hisDataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.test.entity</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <!-- 慎重(推荐禁止) 固定为update -->
                <!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
            </props>
        </property>
    </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>

<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="300" />
    </bean>
        
    <!-- spring 事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="atomikosTransactionManager"/>
        <property name="userTransaction" ref="atomikosUserTransaction" />
        <property name="allowCustomIsolationLevels" value="true"/> 
    </bean>


    <!-- 使用annotation定义事务,对于要加入事物的类,只需对该类加 @Transactional  -->
    <tx:annotation-driven transaction-manager="transactionManager" />


    <!-- hibernate Dao层模板 -->
    <bean id="tmsHibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="tmsSessionFactory"></property>
    </bean>
    
    <bean id="hisHibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="hisSessionFactory"></property>
    </bean>

</beans>

3、在src文件夹下面加入一个transactions.properties文件

复制代码
# SAMPLE PROPERTIES FILE FOR THE TRANSACTION SERVICE# THIS FILE ILLUSTRATES THE DIFFERENT SETTINGS FOR THE TRANSACTION MANAGER# UNCOMMENT THE ASSIGNMENTS TO OVERRIDE DEFAULT VALUES;# Required: factory implementation class of the transaction core.# NOTE: there is no default for this, so it MUST be specified! # com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory        # Set base name of file where messages are output # (also known as the 'console file').## com.atomikos.icatch.console_file_name = tm.out# Size limit (in bytes) for the console file;# negative means unlimited.## com.atomikos.icatch.console_file_limit=-1# For size-limited console files, this option# specifies a number of rotating files to # maintain.## com.atomikos.icatch.console_file_count=1# Set the number of log writes between checkpoints## com.atomikos.icatch.checkpoint_interval=500# Set output directory where console file and other files are to be put# make sure this directory exists!## com.atomikos.icatch.output_dir = ./# Set directory of log files; make sure this directory exists!## com.atomikos.icatch.log_base_dir = ./# Set base name of log file# this name will be  used as the first part of # the system-generated log file name## com.atomikos.icatch.log_base_name = tmlog# Set the max number of active local transactions # or -1 for unlimited.## com.atomikos.icatch.max_actives = 50# Set the default timeout (in milliseconds) for local transactions## com.atomikos.icatch.default_jta_timeout = 10000# Set the max timeout (in milliseconds) for local transactions## com.atomikos.icatch.max_timeout = 300000# The globally unique name of this transaction manager process# override this value with a globally unique name## com.atomikos.icatch.tm_unique_name = tm    # Do we want to use parallel subtransactions? JTA's default# is NO for J2EE compatibility## com.atomikos.icatch.serial_jta_transactions=true                    # If you want to do explicit resource registration then# you need to set this value to false.## com.atomikos.icatch.automatic_resource_registration=true      # Set this to WARN, INFO or DEBUG to control the granularity# of output to the console file.## com.atomikos.icatch.console_log_level=WARN    # Do you want transaction logging to be enabled or not?# If set to false, then no logging overhead will be done# at the risk of losing data after restart or crash.## com.atomikos.icatch.enable_logging=true# Should two-phase commit be done in (multi-)threaded mode or not?# Set this to false if you want commits to be ordered according# to the order in which resources are added to the transaction.## NOTE: threads are reused on JDK 1.5 or higher. # For JDK 1.4, thread reuse is enabled as soon as the # concurrent backport is in the classpath - see # http://mirrors.ibiblio.org/pub/mirrors/maven2/backport-util-concurrent/backport-util-concurrent/## com.atomikos.icatch.threaded_2pc=false# Should shutdown of the VM trigger shutdown of the transaction core too?## com.atomikos.icatch.force_shutdown_on_vm_exit=false
复制代码

4、注入HibernateTemplate模板 
a、为tms库注入HibernateTemplate模板

package com.test.dao.impl;import java.io.Serializable;import java.util.List;import org.hibernate.criterion.DetachedCriteria;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.orm.hibernate3.HibernateTemplate;import org.springframework.orm.hibernate3.support.HibernateDaoSupport;import org.springframework.stereotype.Repository;import com.test.dao.BaseDao;@Repository("tmsDao")public class TmsBaseDaoImpl extends HibernateDaoSupport implements BaseDao{protected HibernateTemplate tmsHibernateTemplate;@Autowiredpublic void setTmsHibernateTemplate(HibernateTemplate tmsHibernateTemplate) {this.tmsHibernateTemplate = tmsHibernateTemplate;super.setHibernateTemplate(tmsHibernateTemplate);}@SuppressWarnings("rawtypes")@Overridepublic List findAll(String hql) throws Exception {return tmsHibernateTemplate.find(hql);}@SuppressWarnings("rawtypes")@Overridepublic List query(String hql,Object value) throws Exception {return tmsHibernateTemplate.find(hql, value);}@Overridepublic <T> Serializable save(T entity) throws Exception {Serializable s = tmsHibernateTemplate.save(entity);return s;}@SuppressWarnings("unchecked")@Overridepublic <T> List<T> find(DetachedCriteria detachedCriteria) throws Exception {return tmsHibernateTemplate.findByCriteria(detachedCriteria);}}

b、为his库注入HibernateTemplate模板

package com.test.dao.impl;


import java.io.Serializable;
import java.util.List;


import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;


import com.test.dao.BaseDao;


@Repository("hisDao")
public class HisBaseDaoImpl extends HibernateDaoSupport implements BaseDao{

protected HibernateTemplate hisHibernateTemplate;

@Autowired
public void setHisHibernateTemplate(HibernateTemplate hisHibernateTemplate) {
this.hisHibernateTemplate = hisHibernateTemplate;
super.setHibernateTemplate(hisHibernateTemplate);
}


@SuppressWarnings("rawtypes")
@Override
public List findAll(String hql) throws Exception {
return hisHibernateTemplate.find(hql);
}

@SuppressWarnings("rawtypes")
@Override
public List query(String hql,Object value) throws Exception {
return hisHibernateTemplate.find(hql, value);
}

@Override
public <T> Serializable save(T entity) throws Exception {
Serializable s = hisHibernateTemplate.save(entity);
return s;
}

@SuppressWarnings("unchecked")
@Override
public <T> List<T> find(DetachedCriteria detachedCriteria) throws Exception {
return hisHibernateTemplate.findByCriteria(detachedCriteria);
}
}

5、编写业务逻辑代码

package com.test.service.impl;


import java.util.List;


import javax.annotation.Resource;


import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


import com.test.dao.BaseDao;
import com.test.entity.HisTest;
import com.test.entity.TmsTest;
import com.test.service.BaseService;


@Service
@Transactional(propagation=Propagation.REQUIRED)
public class BaseServiceImpl implements BaseService{

@Resource
private BaseDao tmsDao;
@Resource
private BaseDao hisDao;

@SuppressWarnings("rawtypes")
@Override
public List findTms() throws Exception {
/*DataSourceContextHolder.setDataSourceType(DataSourceConst.TMS_STRING);
String hql = "from MedicalCard";
return tmsDao.findAll(hql);*/
String hql = "FROM TmsTest";
return tmsDao.findAll(hql);
}

@SuppressWarnings("rawtypes")
public List findHis() throws Exception{
/*DataSourceContextHolder.setDataSourceType(DataSourceConst.HIS_STRING);
String hql = "from Patient where idNo = ?";
return hisDao.query(hql, idNo);*/
String hql = "FROM HisTest";
return hisDao.findAll(hql);
}


@Override
public void save(TmsTest tms, HisTest his) throws Exception {
tmsDao.save(tms);
hisDao.save(his);
}


}

测试方法
package com.test.junit;


import java.util.List;


import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


import com.test.entity.HisTest;
import com.test.entity.TmsTest;
import com.test.service.BaseService;


public class TestService {
private BaseService service;
TmsTest tmsTest;
HisTest hisTest;

@Before
public void init(){
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
BeanFactory factory = (BeanFactory) context;
service = factory.getBean("BaseService",BaseService.class);

tmsTest = new TmsTest();
tmsTest.setName("张三");
hisTest = new HisTest();
hisTest.setName("李四李四李四李四李四李四李四李四李四李四");
}


@Test
public void testDataSource() {
try {
@SuppressWarnings("unchecked")
List<TmsTest> tmsList = service.findTms();
System.out.println("tms============" + tmsList.get(0).getName());
} catch (Exception e) {
e.printStackTrace();
}

try {
@SuppressWarnings("unchecked")
List<HisTest> hisList = service.findHis();
System.out.println("his=================" + hisList.get(0).getName());
} catch (Exception e) {
e.printStackTrace();
}

try {
service.save(tmsTest, hisTest);
} catch (Exception e) {
System.out.println("保存操作失败,事务整体回滚。");
}

}
}

在执行save()方法时会报异常,hisTest的name字段过长;
如果事务生效,save()方法会回滚掉,两张表都不会有save操作,保持事务的原子性;
如果事务不起作用,那么tmsTest插入操作会执行成功,往数据库添加一条记录;hisTest插入操作抛出异常终止向下执行;

Atomikos的集成到此就结束了。

注意
mysql的驱动需要5.1.10版本以上,低版本会出现如下错误:
java.lang.IllegalArgumentException: null source

项目源码下载地址:http://download.csdn.net/detail/miao1726584751/7070675
0 0