`

spring 3 dataSource 事务管理

 
阅读更多

以前在项目中使用spring管理hibernate,配置spring为hibernate提供的事务,注入sessionFactory,开启事务驱动,在类或Service上加入@Transactional(propagation = Propagation.REQUIRED)注解即可,现在在一个项目中,数据访问没使用hibernate,使用的jdbc加连接池,刚开始的时候逻辑比较简单,未使用spring的事务管理,连接池配置如下:

 

<!-- 数据源配置,使用应用内的DBCP数据库连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <!-- Connection Info -->
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- Connection Pooling Info -->
        <property name="maxIdle" value="${dbcp.maxIdle}"/>
        <property name="maxActive" value="${dbcp.maxActive}"/>
        <property name="defaultAutoCommit" value="false"/>
        <property name="timeBetweenEvictionRunsMillis" value="3600000"/>
        <property name="minEvictableIdleTimeMillis" value="3600000"/>
    </bean>

 此时查询都是正常的,在执行insert,update,delete时执行了sql但是数据库记录没有改变,经查证是一个配置项有问题:

 

 

<property name="defaultAutoCommit" value="false"/>

 在代码中我们的Connection没有手动提交,所以不能持久化到数据库,为了省事就在配置文件中配置如下:

<property name="defaultAutoCommit" value="true"/>

 后来随着逻辑的复杂,需要引入事务,所以使用spring的dataSource事务管理,根据spring的官方文档,加入如下配置:

<!-- a PlatformTransactionManager is still required -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- (this dependency is defined somewhere else) -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager"/>

 在代码中的Service方法上加入注解:@Transactional(propagation = Propagation.REQUIRED)

 测试代码如下:

@Transactional(propagation = Propagation.REQUIRED)
    public void saveLoginInfo(String userName, String dateString, String result) {
        String sql = "INSERT INTO prc_mbl_usr_usg (slsprs_id, lgn_dtm, lgn_sts ) VALUES (" + "'" + userName + "'," + "'" + dateString + "'," + "'" + result + "')";
        logger.info(sql);
        toolsDao.insertUtils(sql);
        int m = 1;
        if (m == 1) {
            throw new RuntimeException();
        }
        toolsDao.insertUtils(sql);
    }

 可是在测试后,发现事务不能回滚,第一次的插入数据每次测试都能插入,不能回滚,

当时很纳闷,究竟是什么原因导致事务不能生效呢?

起初怀疑是事务配置有问题,看了官方文档,google好了好多,发现配置没问题,

在后来耐着性子从头到尾看一下配置文件,并思考一下整个测试方法的执行过程,

然后恍然大悟,发现连接池的配置:

<property name="defaultAutoCommit" value="true"/>

 也就是说jdbc虽然在事务里,但是自动提交了,所以spring事务无法让数据库回滚,把true改为false后,经测试数据库正常回滚!

 

===============================================================================================================================================================================

分割线以上是第一次写这篇文章,下面是后来的修改!

 

上面的文章是错误的,把配置defaultAutoCommit改为false不是数据库回滚了,而是Connection根本没有提交,发现insert,update语句执行之后根本持久化不到数据库;下面是toolsDao类insertUtils的方法:

    //执行插入操作
    public String insertUtils(String sql) {
        {
            Connection con = null;
            try {
                con = getConnection();
            } catch (SQLException e) {
                logger.error("connect failed", e);
            }
            Statement stmt = null;
            if (con != null) {
                try {
                    stmt = con.createStatement();
                } catch (SQLException e3) {
                    logger.error("createStatement create failed", e3);
                }
            }
            assert stmt != null;
            //(2)发送SQL语句到数据库中
            try {
                stmt.executeUpdate(sql);
                return "success";

            } catch (SQLException e4) {
                logger.error("sql error,or no resSet", e4);
            } finally {
                close(null, stmt, con);
            }
        }
        return "failed";
    }

    //获取Connection
    public Connection getConnection() throws SQLException {
        basicDataSource.getInitialSize();
        return basicDataSource.getConnection();
    }

 如上是我执行插入的操作,这里直接使用连接池,从连接池里获取连接,本意是想使用spring的事务进行事务管理,正确的配置了事务,但是没有生效,为什么?

下面是我个人的分析,如有不正确,请指教:

在真正的插入操作时,使用的是Connection,通过Connection获取的Statement,connection的默认提交方式是true,也就是connection会自动提交的,从上面的代码看,每当执行完插入操作后,会关闭Connection,这里不是真正的关闭,起始这里得到的Connection是一个代理对象,这里的close()方法也就是把Connection还给连接池,标记为空闲,供其他请求使用,再还给连接池之前,connection把数据提交到数据库了,spring的事务是无法回滚的;

我尝试着在上面的代码获取到connection后加了一行代码,con.setAutoCommit(false);让connection不自动提交,让spring管理,可是测试结果是数据根本无法持久化到数据库,所以我感觉要使用spring的事务管理数据源,这种代码实现方式是行不通的,所以我在代码中引入了JdbcTemplate:

从新定义一个dao层的基类(测试代码,就不定义dao接口层了,并且在基类里只实现了一个insert方法,如有需要可以自己实现):

public class BaseDao extends JdbcDaoSupport {

    public void insert(String sql) {
        this.getJdbcTemplate().execute(sql);
    }

}

 子类dao:

public class BaseDaoImipl extends BaseDao {
}

 在spring配置文件中的配置:

    <context:annotation-config />
    <!-- 使用annotation 自动注册bean,并保证@Required,@Autowired的属性被注入 -->
    <context:component-scan base-package="com.intel.store"/>

    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager"/>

    <!-- a PlatformTransactionManager is still required -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- (this dependency is defined somewhere else) -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 被事务管理的数据源 -->
    <bean id="baseDao" class="com.intel.store.dao.BaseDao" abstract="true">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 配置继承 -->
    <bean id="baseDaoImpl" class="com.intel.store.dao.BaseDaoImipl" parent="baseDao">
    </bean>

 这里使用基类baseDao是为了提取公共操作,把重复的CRUD封装到基类中,子类只要调用即可;

 

测试代码:

    //junit测试代码
    @Test
    public void testTransaction() {
        System.out.println(AopUtils.isAopProxy(loginServiceImpl));
        System.out.println(loginServiceImpl.getClass().getName());
        loginServiceImpl.saveLoginInfo("2000040", "2013-09-10 00:00:00.000", "2000040|张美霞test");
    }
    
    //service方法中的业务方法
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveLoginInfo(String userName, String dateString, String result) {
        String sql = "INSERT INTO prc_mbl_usr_usg (slsprs_id, lgn_dtm, lgn_sts ) VALUES (" + "'" + userName + "'," + "'" + dateString + "'," + "'" + result + "')";
        logger.info(sql);
        this.baseDaoImpl.insert(sql);

        int m = 0;

//        if (m == 0) {
//            throw new RuntimeException("出错了!");
//        }
        this.baseDaoImpl.insert(sql);
    }

 放开注释,第一次插入的数据正常回滚,不放开注释,两条数据顺利插入到数据库!

分享到:
评论

相关推荐

    spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制

    spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制

    spring五种事务配置demo

    测试spring事务管理 搭建了ssh框架的web工程 本工程用到的数据库表很简单 user(id, name) 可自行创建 本例所有的事务放在service层进行管理,方法中间抛出运行时异常以测试是否回滚 Spring配置文件中关于事务...

    dynamic-datasource-spring-boot-starter-v3.5.1.zip

    Dynamic-Datasource (opens new window)- 基于 SpringBoot 的多数据源组件,功能强悍,支持 Seata 分布式事务。 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 支持数据库敏感配置信息 ...

    Spring + Hibernate + Struts 事务配置小例子(带提示框等小技巧)

    前几天搞 Spring + Hibernate + Struts 事务配置 ,网上找了好多资料,不过好无语,大多都是 Ctrl + V,浪费俺的宝贵时间 现在我总结配出一套,给大家参考参考,可能有不足,请大家多多交流。 附:内有弹出...

    dynamic-datasource-spring-boot-starter-v3.5.1.tar.gz

    Dynamic-Datasource (opens new window)- 基于 SpringBoot 的多数据源组件,功能强悍,支持 Seata 分布式事务。 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 支持数据库敏感配置信息 ...

    Spring事务配置的五种方式

    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、TransactionManager这两部分只是会...

    mavin spring4 mvc mybatis 整合 带事务,REST风格

    从csdn的同胞那下的,但是没法用,做了些调整后已能使用。开发工具为myeclipse2013,tomcat7,mysql。同时加入了事务控制。

    【分布式事务----LCN】LCN原理及使用方式.docx

    TxClient的代理连接池实现了javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭以后TxClient连接池将执行"假关闭"操作,等待TxManager协调完成事务以后在关闭连接。 对于代理连接池的优化 自动超时...

    Spring 事务隔离与事务传播的详解与对比

    今天一起学习一下Spring的事务管理。Spring的事务管理分为声明式跟编程式。声明式就是在Spring的配置文件中进行相关配置;编程式就是用注解的方式写到代码里。 Spring配置文件中关于事务配置总是由三个组成部分,...

    spring+jotm 多数据源事务管理(三)JNDI+Tomcat

    spring+jotm 多数据源事务管理(三)JNDI+Tomcat 首先需要将jotm相关jar包加入到tomcat中,如下: o jotm.jar o jotm_jrmp_stubs.jar o ow_carol.jar o jta.jar o jta-spec1_0_1.jar o jts1_0.jar o objectweb-...

    datasource:持久层

    ①JDK8 ②Java直接整合MyBatis在Java中直接使用MyBatis框架需要读取配置,手动构造SqlSessionFactory / SqlSession ③Spring整合MyBatis(传统写法)事务 ss_mybatis ①JDK6 ②Spring/ SpringMVC整合原生MyBatis...

    Spring事务配置的五种方法

    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、TransactionManager这两部分只是会...

    dynamic-datasource-spring-boot-starter:springboot的动态数据源多数据源动态数据源主从分离读写分离分布式事务https:dynamic-datasource.com

    一个基于springboot的快速集成多数据源的启动器简介dynamic-datasource-spring-boot-starter是一个基于springboot的快速集成多数据源的启动器。其支持Jdk 1.7 +,SpringBoot 1.4.x 1.5.x 2.xx。文件| 文献资料|特性...

    Spring事务配置5种方式

    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、TransactionManager这两部分只是会...

    Spring 2.0 开发参考手册

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring ...

    Spring的学习笔记

    (三) XML文件形式配置Spring事务管理 37 四、 HibernateTemplate 38 (一) HibernateTemplate 38 (二) HibernateDaoSupport 39 第十一课:Spring整合-SSH 40 一、 第一步:加入jar包(需要的jar包列表) 40 二、 第二步...

    spring.doc

    5.1.8.1Spring的事务管理器 117 5.1.8.2Spring事务的传播属性 117 5.1.8.3Spring事务的隔离级别 117 拓展: 118 5.1.8.4以XML配置的 形式 119 拓展: 120 5.1.8.5以注解方式配置 125 拓展: 127 5.1.9使用CGLIB以XML...

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring JDBC包结构...

    分布式事务实践 解决数据一致性

    介绍了Spring的事务机制、事物抽象、内部事务和外部事物,以及常用的几种事务管理的实现,包括DataSource、JPA、JMS、JTA都通过实例进行说明。还有XA以及两阶段提交,并通过实例演示了使用JTA,通过两阶段提交,实现...

    基于 SpringBoot 多数据源 动态数据源 主从分离 快速启动器 支持分布式事务.zip

    引入dynamic-datasource-spring-boot-starter。... &lt;artifactId&gt;dynamic-datasource-spring-boot3-starter ${version} 配置数据源。 spring: datasource: dynamic: enabled: true #启用动态数据源,默认true

Global site tag (gtag.js) - Google Analytics