Spring结合hibernate和jdbcTemplate多数据源事物问题

首先说明我的环境Spring3.2.8+hibernate3.6.10.Final+tomcat7

在写这篇博客前在网上查询了相关资料 ,网上给出的结论是:如果有多个相同的数据源,开启了多个事务,使用spring的声明式事务(AOP配置:1、xml配置 2、注解Transaction 两者都是。关于编程式事务就自行百度)管理事务,一旦发生RunTimeException异常,那么多个事务都会回滚。 如果是多个不相同的数据源(比如oracle不同用户、oracle数据源和SqlServer数据源这样的),开启了多个事务, 使用使用spring的声明式事务管理事务,一旦发生RunTimeException异常,多个事务不能同时回滚

上面就是网上给出的结论,在Tomcat中如果让spring支持不同数据源事务一致性,必须用第三方工具 比如 Atomikos 和Jotm(或者换一个应用服务器 比如JBOSS)。下面给出我的测试结果

配置多个数据

我配置了三个数据源(每单个数据源又分为hibernate和jdbcTemplate)

数据源1、 dataSourceZhw:
数据库是oracle,用户是zhw
构建的hibernate和jdbcTemplate模板是 hibernateTemplate_zhw和jdbcTemplate_zhw
构建的两个事务管理器是:hiernateTx_zhw和jdbcTx_zhw

<bean id="dataSourceZhw" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.oracle_driverClassName}" />
        <property name="url" value="${jdbc.oracle_url}" />
        <property name="username" value="${jdbc.oracle_username}" />
        <property name="password" value="${jdbc.oracle_password}" />
    </bean>


<!-- 定义hibernate模板和事务管理 -->
    <bean id="hibernateTemplate_zhw" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactoryZhw"></property>
    </bean>

    <bean id="txManager_zhw_hibernate" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactoryZhw" />
        <qualifier value="hiernateTx_zhw"></qualifier>
    </bean>

    <tx:annotation-driven transaction-manager="txManager_zhw_hibernate" />


    <!-- 定义jdbcTemplate模板和事务管理 -->
    <bean id="jdbcTemplate_zhw" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg  name="dataSource" ref="dataSourceZhw"></constructor-arg>
    </bean>

    <bean id="txManager_zhw_jdbc" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSourceZhw"/>
      <qualifier value="jdbcTx_zhw"></qualifier>
    </bean>

    <tx:annotation-driven transaction-manager="txManager_zhw_jdbc" />

数据源2、 dataSourceHwj:
数据库是oracle,用户是hwj

构建的hibernate和jdbcTemplate模板是 hibernateTemplate_hwj和jdbcTemplate_hwj
构建的两个事务管理器是:hiernateTx_hwj和jdbcTx_hwj

<bean id="dataSourceHwj" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.oracle_driverClassName}" />
        <property name="url" value="${jdbc.oracle_url_hwj}" />
        <property name="username" value="${jdbc.oracle_username_hwj}" />
        <property name="password" value="${jdbc.oracle_password_hwj}" />
    </bean>

<!-- 定义hibernateTemplate模板和事务管理 -->
    <bean id="hibernateTemplate_hwj" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactoryHwj"></property>
    </bean>

    <bean id="txManager_hwj_hibernate"  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactoryHwj" />
        <qualifier value="hiernateTx_hwj"></qualifier>
    </bean>
    <tx:annotation-driven transaction-manager="txManager_hwj_hibernate"/>


    <!-- 定义jdbcTemplate模板和事务管理 -->
    <bean id="jdbcTemplate_hwj" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg  name="dataSource" ref="dataSourceHwj"></constructor-arg>
    </bean>

    <bean id="txManager_hwj_jdbc" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSourceHwj"/>
      <qualifier value="jdbcTx_hwj"></qualifier>
    </bean>

    <tx:annotation-driven transaction-manager="txManager_hwj_jdbc" />

数据源3、 dataSourceMs:
数据库是sqlserver,用户是sa

构建的hibernate和jdbcTemplate模板是 hibernateTemplate_ms和jdbcTemplate_ms
构建的两个事务管理器是:hiernateTx_ms和jdbcTx_ms

<bean id="dataSourceMs" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

<!-- 定义hibernateTemplate模板和事务管理 -->
    <bean id="hibernateTemplate_ms" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactoryMs"></property>
    </bean>

    <bean id="txManager_ms_hibernate" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactoryMs" />
        <qualifier value="hiernateTx_ms"></qualifier>
    </bean>
    <tx:annotation-driven transaction-manager="txManager_ms_hibernate" />


    <!-- 定义jdbcTemplate模板和事务管理 -->
    <bean id="jdbcTemplate_ms" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg  name="dataSource" ref="dataSourceMs"></constructor-arg>
    </bean>

    <bean id="txManager_ms_jdbc" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSourceMs"/>
      <qualifier value="jdbcTx_ms"></qualifier>
    </bean>

    <tx:annotation-driven transaction-manager="txManager_ms_jdbc" />

模拟服务层环境(hibernate进行操作)

首先BaseDao是

package cn.soft.test.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.orm.hibernate3.HibernateTemplate;

/**
 * @author Administrator
 *
 */
public abstract class BaseDao {

    @Autowired
    @Qualifier("hibernateTemplate_zhw")
    public HibernateTemplate zhwHt;

    @Autowired
    @Qualifier("jdbcTemplate_zhw")
    public NamedParameterJdbcTemplate zhwJdbc;


    @Autowired
    @Qualifier("hibernateTemplate_hwj")
    public HibernateTemplate hwjHt;


    @Autowired
    @Qualifier("jdbcTemplate_hwj")
    public NamedParameterJdbcTemplate hwjJdbc;


    @Autowired
    @Qualifier("hibernateTemplate_ms")
    public HibernateTemplate msHt;


    @Autowired
    @Qualifier("jdbcTemplate_ms")
    public NamedParameterJdbcTemplate msJdbc;

}

服务层

扫描二维码关注公众号,回复: 1738843 查看本文章
/**
     * 测试添加人员到三个数据库
     * 这三个数据源都是hibernate
     * 
     * 并且抛出异常,看看回滚情况
     * 
     * 
     * 如果出现异常,那么三个数据库都回滚(即使没有指定使用那个事务管理器)
     * 
     */
    @Transactional
    public void addPerson(){

            Person2 p2 = new Person2();//数据源zhw的实体

            Person1 p1 =new Person1();//数据源hwj的实体

            User p3 = new User();//数据源SqlServer对应的实体

            p2.setId(GeneNm.getNmByUUID());
            p2.setName("zhw");

            p1.setId(GeneNm.getNmByUUID());
            p1.setName("hwj");

            p3.setId(GeneNm.getNmByUUID());
            p3.setName("sqlserver");

            dao.zhwHt.save(p2);//保存到数据源zhw对应的数据库

            dao.hwjHt.save(p1);//保存到数据源hwj对应的数据库

            dao.msHt.save(p3);//保存到数据源sqlserver对应的数据库

            int i =1/0;//故意制造一个异常

    }

执行之后放出结果: 最后这三个实体都没有保存成功!!
这个结果刚开始我也是很惊讶,经过我的研究后,下面是我的解释

  • @Transactional 这个注解使用了,就注定会使用一个事务管理器,而且默认的级别是propagation=Propagation.REQUIRED,但是如果不通过显示的指定会使用那个事务管理器的(毕竟我定义了6个事务管理器),这个问题可以通过将LOG4J的日志级别设置为log4j.logger.org=debug
    通过 控制台使用的事务信息来确定,最后我发现结果是这样的,如果不是显示指定,谁先定义的就先使用谁, 别以为上面是使用的hibernate来操作的最起码应该用hiernateTx_zhw、hiernateTx_hwj、hiernateTx_ms 其中一个,不是的如果你把jdbcTx_zhw这个事务管理器先定义,照样会先使用jdbcTx_zhw这个事务管理器

  • Spring声明式事务,会使用动态代理生产一个代理对象,在进行调用真正的方法的时候会先打开一个事务,当执行保存操作的时候,会把保存生成的事务加入到 第一个事务中,当执行第二个保存操作的时候,同样会把保存操作生成的事务加入到第一个事务中,第三个同理(每个保操作都会生产一个事务)。 这个时候这些实体 只是从transient—>detached(因为有了主键),只有当提交事务的时候才会变成persistent(持久化状态),但是因为 int i =1/0,直接产生了异常,这个时候hibernate根本就不会提交,生成sql语句, 这里很重要的一点是他会让Transactional 注解使用的那个数据源对应的事务回滚。

其实网上说的也没错,当产生异常的时候确实不是多个不同数据源对应的事务同时回滚,只会 回滚Transactional 注解使用的那个数据源对应的事务。但是hibernate比较特殊,如果发生异常,hibernate根本就不会产生sql语句把实体 持久化

这里可以猜测,如果使用jdbcTemplate来进行操作,每执行一个操作,会立马产生sql语句,进行数据库更新,这样到最后发生异常,也只会回滚Transactional 注解使用的那个数据源对应的事务,另外的事务就不会回滚

下面我打印控制台的日志

还没执行保存操作前

[DEBUG] 2016-09-24 14:12:06 :Returning cached instance of singleton bean 'txManager_hwj_hibernate'//这里没有显示指定,使用了txManager_hwj_hibernate这个事务管理器

[DEBUG] 2016-09-24 14:12:06 :Creating new transaction with name [cn.soft.test.service.PersonService.addPerson]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''//创建了一个事务

[DEBUG] 2016-09-24 14:12:06 :Opened new Session [org.hibernate.impl.SessionImpl@79ba71f] for Hibernate transaction//打开一个session

[DEBUG] 2016-09-24 14:12:06 :Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@79ba71f]//session 准备

[DEBUG] 2016-09-24 14:12:06 :Exposing Hibernate transaction as JDBC transaction [jdbc:oracle:thin:@192.168.1.120:1521:myfirstdb, UserName=HWJ, Oracle JDBC driver]//这个就是事务对应的数据源的信息

执行第一个save操作后(dao.zhwHt.save(p2);)

[DEBUG] 2016-09-24 14:17:51 :Opening Hibernate Session //打开一个session

[DEBUG] 2016-09-24 14:17:51 :Registering Spring transaction synchronization for new Hibernate Session

[DEBUG] 2016-09-24 14:17:51 :Found thread-bound Session for HibernateTemplate
//上面两句话大概就是把创建的事务加入到 当前事务中(当前事务是保存在当前线程中的)

[DEBUG] 2016-09-24 14:17:51 :Not closing pre-bound Hibernate Session after HibernateTemplate//不关闭预绑定的session,供给同样数据源获取session

执行第二个save(dao.hwjHt.save(p1);)

//这里没有打开一个session 就是因为Transactional 对应的数据源(hwj)就是这个,在执行所有保存任务前已经打开了一个session 就是[org.hibernate.impl.SessionImpl@79ba71f]

[DEBUG] 2016-09-24 14:23:44 :Found thread-bound Session for HibernateTemplate////上面两句话大概就是把创建的事务加入到 当前事务中(当前事务是保存在当前线程中的)

[DEBUG] 2016-09-24 14:23:44 :Not closing pre-bound Hibernate Session after HibernateTemplate//不关闭预绑定的session,供给同样数据源获取session

执行第三个保存(dao.msHt.save(p3);)

[DEBUG] 2016-09-24 14:27:45 :Opening Hibernate Session
[DEBUG] 2016-09-24 14:27:45 :Registering Spring transaction synchronization for new Hibernate Session
[DEBUG] 2016-09-24 14:27:45 :Found thread-bound Session for HibernateTemplate
[DEBUG] 2016-09-24 14:27:45 :Not closing pre-bound Hibernate Session after HibernateTemplate
//这里和第一个保存时一模一样的,不在阐述

发生异常后

[DEBUG] 2016-09-24 14:29:08 :Initiating transaction rollback
[DEBUG] 2016-09-24 14:29:08 :Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl@79ba71f]//回滚Transactional 对应数据源的session

//下面就是关闭资源的操作
[DEBUG] 2016-09-24 14:29:08 :Closing Hibernate Session
[DEBUG] 2016-09-24 14:29:08 :Closing Hibernate Session
[DEBUG] 2016-09-24 14:29:08 :Closing Hibernate Session [org.hibernate.impl.SessionImpl@79ba71f] after transaction
[DEBUG] 2016-09-24 14:29:08 :Closing Hibernate Session

好了整个流程结束了,所以使用hibernate算是可以模拟不同数据源 在spring事务管理下同时回滚,其实只是模拟,真实情况却不是。

模拟服务层环境(jdbcTemplate进行操作)

服务层核心代码

/**
     * 测试添加人员到三个数据库
     * 操作这三个数据源都是jdbcTemplate
     * 
     * 并且抛出异常,看看回滚情况
     * 
     * 
     * 如果出现异常,只有@Transactional 注解使用的那个数据源的事务会回滚
     * 
     */
    @Transactional(value="jdbcTx_ms")
    public void addPerson2(){

            Person2 p2 = new Person2();//数据源zhw的实体

            Person1 p1 =new Person1();//数据源hwj的实体

            User p3 = new User();//数据源SqlServer对应的实体

            p2.setId(GeneNm.getNmByUUID());
            p2.setName("zhw");



            p1.setId(GeneNm.getNmByUUID());
            p1.setName("hwj");

            p3.setId(GeneNm.getNmByUUID());
            p3.setName("sqlserver");


            dao.saveOrupdate(dao.zhwJdbc, "insert into t_person(id,name) values ('"+p2.getId()+"','"+p2.getName()+"') ");//保存到数据源zhw对应的数据库

            dao.saveOrupdate(dao.hwjJdbc, "insert into t_person(id,name) values ('"+p1.getId()+"','"+p1.getName()+"') ");//保存到数据源hwj对应的数据库

            dao.saveOrupdate(dao.msJdbc, "insert into t_user(id,name) values ('"+p3.getId()+"','"+p3.getName()+"') ");//保存到数据源sqlserver对应的数据库

            int i =1/0;

    }

结果就是只有Transaction使用的那个数据源回滚了

下面我打印控制台的日志

保存前

[DEBUG] 2016-09-24 14:51:04 :Creating new transaction with name [cn.soft.test.service.PersonService.addPerson2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 'jdbcTx_ms' //使用的事务管理器

[DEBUG] 2016-09-24 14:51:04 :Acquired Connection [jdbc:sqlserver://127.0.0.1:1433;…… UserName=sa, Microsoft JDBC Driver 4.0 for SQL Server] for JDBC transaction //获取事务管理器对应的Connection

[DEBUG] 2016-09-24 14:51:04 :Switching JDBC Connection [jdbc:sqlserver://127.0.0.1:1433;…… UserName=sa, Microsoft JDBC Driver 4.0 for SQL Server] to manual commit  //选择事务管理器对应的Connection

第一次保存

[DEBUG] 2016-09-24 14:55:23 :Executing prepared SQL update

[DEBUG] 2016-09-24 14:55:23 :Executing prepared SQL statement [insert into t_person(id,name) values ('a49fa36c-4cf8-4a09-a0a7-','zhw') ]//执行sql语句

[DEBUG] 2016-09-24 14:55:23 :Fetching JDBC Connection from DataSource//获取新的Connection 
[DEBUG] 2016-09-24 14:55:23 :Registering transaction synchronization for JDBC Connection//新事务 加入到 第一个事务中

[DEBUG] 2016-09-24 14:55:23 :SQL update affected 1 rows

第二次保存

//和第一次保存一模一样
[DEBUG] 2016-09-24 14:58:46 :Executing prepared SQL update

[DEBUG] 2016-09-24 14:58:46 :Executing prepared SQL statement [insert into t_person(id,name) values ('da9a8c6b-b8e5-4e96-a663-','hwj') ]

[DEBUG] 2016-09-24 14:58:46 :Fetching JDBC Connection from DataSource

[DEBUG] 2016-09-24 14:58:46 :Registering transaction synchronization for JDBC Connection

[DEBUG] 2016-09-24 14:58:46 :SQL update affected 1 rows

第三次保存

//发现根据就没有获取Connection 因为保存之前已经获取了
[DEBUG] 2016-09-24 15:03:35 :Executing prepared SQL update

[DEBUG] 2016-09-24 15:03:35 :Executing prepared SQL statement [insert into t_user(id,name) values ('f58a20a8-0f88-4bea-824d-','sqlserver') ]

[DEBUG] 2016-09-24 15:03:35 :SQL update affected 1 rows

出现异常后

[DEBUG] 2016-09-24 15:05:40 :Returning JDBC Connection to DataSource

[DEBUG] 2016-09-24 15:05:40 :Returning JDBC Connection to DataSource

[DEBUG] 2016-09-24 15:05:40 :Initiating transaction rollback

[DEBUG] 2016-09-24 15:05:40 :Rolling back JDBC transaction on Connection [jdbc:sqlserver://127.0.0.1:1433;…… UserName=sa, Microsoft JDBC Driver 4.0 for SQL Server] //回滚Transction对应的数据源

[DEBUG] 2016-09-24 15:05:40 :Releasing JDBC Connection [jdbc:sqlserver://127.0.0.1:1433;…… UserName=sa, Microsoft JDBC Driver 4.0 for SQL Server] after transaction

[DEBUG] 2016-09-24 15:05:40 :Returning JDBC Connection to DataSource

好了,有时间我会写 利用atomikos真正实现不同数据源事务问题

猜你喜欢

转载自blog.csdn.net/chuxue1989/article/details/52650453
今日推荐