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);
    }

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

猜你喜欢

转载自abc08010051.iteye.com/blog/1969545
今日推荐