`

spring结合mysql事务注解@Transactional不起作用的问题

 
阅读更多

最近遇到的一个比较诡异的问题,貌似各种配置都正确了,事务不起效。

首先resin服务器的配置文件连接数据库的配置如下:

<database>
		<jndi-name>jdbc/bbs7_app</jndi-name>
		<driver>
				<type>com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource</type>
				<url>jdbc:mysql://192.168.74.5:3310/bbs7_pc_app?useUnicode=true&amp;characterEncoding=GBK</url>
				<user>root</user>
				<password>root</password>
		</driver>
		<prepared-statement-cache-size>30</prepared-statement-cache-size>
		<max-connections>30</max-connections>
		<max-idle-time>120s</max-idle-time>
	</database>

 

spring的配置文件配置了注解开启:

<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<context:component-scan base-package="cn.pconline.bbs7">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />  
    </context:component-scan>

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

 要加事务的public方法也已经加上了注解(注解只对public方法有效)

@Transactional
    public int updateTopicAndGetFloor(User user, Topic topic, Date now) {
    	int floor = 0;
    	if (!topic.isNoUp()) {
            topic.setLastPostAt(now);
        }
        topic.setLastPosterId(user.getUid());
        StringBuilder sql = new StringBuilder("UPDATE ").append(TABLE_NAME).append(
                " SET replyCount=replyCount + 1,  lastPosterId=?").append(
                ", lastPostAt=?, updateAt=?, floor=floor + 1 WHERE tid=?");
        int result = topicXdb.getJdbcTemplate(topic.getFid()).update(topicXdb.xsql(topic.getFid(), sql.toString()),
                topic.getLastPosterId(), topic.getLastPostAt(),
                new Date(), topic.getTid());
        if (result > 0) {
            removeFromCache(topic);
      // throw new RuntimeException("测试事务异常");
            floor = getFloorFromDB(topic);
        }
        return floor;
    }

 但是,手动测试让方法抛出一个RuntimeException的时候,数据没有回滚。

排除了半天,发现mysql的事务是默认提交的,把mysql的全局事务默认提交关闭后(如何设置事务关闭,参考这里:http://breezylee.iteye.com/admin/blogs/2052861),用root权限的用户登录,还是无效(mysql的bug,root用户事务默认提交),于是,换成其它一般权限的用户连接,发现还是不行(此时一般用户的事务,不是默认提交了)。

听一个牛人同事说,mysql的驱动com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource,不支持事务的,于是换成了com.mysql.jdbc.Driver,所以,数据源驱动配置改为如下:

<database>
		<jndi-name>jdbc/bbs7_app</jndi-name>
		<driver>
				<type>com.mysql.jdbc.Driver</type>
				<url>jdbc:mysql://192.168.74.5:3310/bbs7_pc_app?useUnicode=true&amp;characterEncoding=GBK</url>
				<user>bbs7_pc_app</user>
				<password>bbs7_pc_app</password>
		</driver>
		<prepared-statement-cache-size>30</prepared-statement-cache-size>
		<max-connections>30</max-connections>
		<max-idle-time>120s</max-idle-time>
	</database>

 改成这样之后,方法就有了事务控制了,抛异常之后数据可以正常回滚。

 

更正下,com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource这个驱动并不是不支持事务,上面的实验证明,貌似和spring的 jdbcTemplate操作、声明式事务@Transactional有点冲突。如果改用纯的jdbc操作,这个驱动还是支持事务的。

Connection conn = dataSource.getConnection();这样每次都是拿到一个新的连接,事务只在同一个连接里面有效,如果是多个连接,就是分布式事务,要换另一些解决方案。参考这里:

http://www.iteye.com/problems/89655

 

 

----------------------2014.8.8------------------

在这个环境下:

(1)mysql关闭自动提交

(2)程序没有加事务控制

(3)mysql的驱动用 com.mysql.jdbc.Driver

(4)用非root权限的普通权限的用户连接mysql

用spring的批量更新方法:jdbcTemplate.batchUpdate(sql.toString(), batch),

方法是执行成功了,但数据库没有更新到,就是说事务没有提交成功,如果执行完上面的batchUpdate后再手动commit,事务是可以提交成功的。

同样的环境,如果把驱动换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource这个之后,不用手动commit,事务也可以提交成功。

具体原因不明,貌似是com.mysql.jdbc.Driver这个驱动不支持批量更新的事务。

 

 

分享到:
评论
1 楼 q280499693 2015-04-14  
我遇到个比你这更奇葩的事情,就是我用spring控制事务,mysql5.0以上的版本事务都控制不住,mysq5.0事务控制成功,求解。

相关推荐

Global site tag (gtag.js) - Google Analytics