Spring事务的概述
事务(Transaction):在数据库中,可以保持一系列的数据操作要么全部执行成功,要么全部执行失败的机制!在基于Spring JDBC的编程中,只需要为业务方法加上
@Transactional
注解,就可以使得该业务方法中的多条数据操作是有事务的保障的,这多条数据操作要么全部成功,要么全部失败,不会出现成功一半且失败一半的问题!
主要原理是在提交2次sql事务时会现在内存中操作,然后没有异常了再提交到数据库,如果在内存中报错了那么就不提交到数据库以实现回滚.
摘自成大佬笔记-.-
遇到问题:
在spring配置业务相关方法上添加了@Transactional
注解后无法做到异常的回滚大致情况如下:
有一段这样的代码
//该方法将数据库中指定用户ID(非主键,可存在多条信息)的某一字段设置为0
Integer rows = addressMapper.updateNonDefaultByUid(uid);
if(rows<1) {
throw new UpdateException("[1]");
}
//该方法将数据库中指定一条数据的某一字段(与以上字段为同一字段)值设置为1
rows = addressMapper.updateDefaultByAid(aid, username, now);
if(rows!=1) {
throw new UpdateException("[2]");
}
以上在同一个业务中对一张表做了2次的update操作其中第一次操作为正常操作不会出现异常,可以使该用户ID下所有该字段值设置为0,而预想中第二条语句应将该用户某一行的该数据设置为1,但是此时设置为1的这条语句抛出了异常不会像预想中的方式进行update,并且会抛出一个运行中的异常,那么此时因为有事务的机制,以上第一次执行设置为0的操作应该被回滚,但是事实是这一条数据操作已经被提交到了数据库.然后第二段操作方法才抛出了异常.
排查与测试
首先,在两行代码中添加了一个延时10秒的方法TimeUnit.MILLISECONDS.sleep(10000)
.
而后进行观察发现开始延时前数据库中的数据已经发生了变化!
最后等待延迟结束第二个语句抛出了异常,但是这时发现设置为0的数据并未变化(实际已提交的事务所以这里不会变化是正常的,若要说最正常的情况应该第一个方法不应该提交到数据库,也就是数据库的数据不会改变)
此时检查所有Java代码均未发现异常!
所以将问题指向数据库方面!
查看数据库配置my.ini发现以下配置发现mysql默认引擎为myisam:
default_storage_engine=MyISAM
同时查看建表情况:
show create table 表明
发现该表为myisam引擎!非innodb!所以确定事务未生效的问题就在于mysql中这张表的引擎
myisam是不支持事务的!在要使用事务的时候应该使用innodb引擎来建表
处理异常
dump数据表并做修改
大致语句如下
mysqldump -uroot -pxxx db名 >> dump.sql
将建表后的ENGINE=myisam改为以下
ENGINE=InnoDB
< 错误>的操作演示
为了做一个备份,我将数据库的物理文件改名,并通过语句重新建了一个同名数据库!(这是个错误的操作
高版本mysql有元数据!会导致后续操作出现各种问题)
然后进入该数据库我将刚才dump好的数据表刷入该数据库.此时发现刷库报错了告知我表已存在(因为该表还在mysql的元数据中比如information_schema.TABLES里,当然不止一处)!这时候打消了我去处理元数据的想法!
最终解决方案(可能有点小过分大佬勿喷- -!同时求教优解)
最终使用了一个方法是,在另外无用的数据库中建立了同名且同引擎的相同数据表然后复制其物理文件到要使用的数据库中然后drop 重复名的表.
等待都drop后重新刷入dump数据!此时刷入正常!
然后再次测试事务java代码,之前出现事务的异常已不存在!
小结
- myisam引擎的表不支持事务,在建表时尽量使用innodb,两种引擎各有各的好处,myisam个人认为最大的优势在于效率与修复容易,但是效率这一优势,已经慢慢的被innodb赶上甚至超越(innodb做些小调优)
- 高版本的mysql操作物理文件会导致元数据异常!
- 在一个方法中多次增删改数据时必须使用事务!