以前在项目中使用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);
}
放开注释,第一次插入的数据正常回滚,不放开注释,两条数据顺利插入到数据库!