Spring事务管理配置文件问题排查
在开发中,遇到了sql语句报错,但是并没有回滚的情况。
经过几天的排查,终于找到了事务没有回滚的原因。
原来的项目用的是informix的数据库,原来针对事务回滚的机制都是好用的。我本地用的是mysql数据库。
先将程序代码与spring-mybatis.xml配置文件拿过来:
1、程序代码:
这个问题是在验证增删改查返回值时发现的。
两个操作,删除时,因为关联了外键,所以会报错,此时正常情况更新的语句也会回滚,但是并没有。
/** *@Author: Administrator on 2020/3/12 15:15 *@param: *@return: *@Description:查询同步情况 */ @Override public PageInfo getSyncstatusPages(Syncstatus vo, int pageNo, int pageSize) { PageHelper.startPage(pageNo, pageSize); /* //查看增删改查的返回值 //1新增:返回值自己定义,可以是void,int //1-1新增一条数据:插入成功,返回值为1 int insert_success1 = yylfHttpServletMapper.insert('8', '2', '1'); //1-2新增多条数据:插入成功,返回值为插入的数据条数,当有一条数据错误时,所有数据都会插入失败 int insert_success2 = yylfHttpServletMapper.insert_duotiao('7'); String insert_success3 = yylfHttpServletMapper.insert_duotiao_String('7');//不支持返回值为String类型 //1-3新增一条数据:插入失败:主键冲突,会直接报异常 int insert_failed = yylfHttpServletMapper.insert('1', '2', '1'); //1-4插入null:属性为null,如果表中所有字段允许为null,插入一条所有值均为null的数据 Syncstatus syncstatus1 = null; yylfHttpServletMapper.insertSyncstatus(syncstatus1); //1-5插入一个没有赋值的对象:属性为null,如果表中所有字段允许为null,插入一条所有值均为null的数据 Syncstatus syncstatus2 = new Syncstatus(); yylfHttpServletMapper.insertSyncstatus(syncstatus2);*/ /*//2删除:返回值自己定义,可以是void,int //2-1删除成功:没有数据:返回值为0 int delete_success1 = yylfHttpServletMapper.delete('0'); //2-2删除成功:有多条数据:返回值为删除的数据条数 int delete_success2 = yylfHttpServletMapper.delete_systemcode('2');*/ //2-3删除失败:例如有外键:报异常 //3更新:返回值自己定义,可以是void,int //3-1更新成功:没有数据,返回值为0 //int update_no = yylfHttpServletMapper.update_no('0'); //3-2更新成功:有多条数据,返回更新的数据条数 int update_duotiao = yylfHttpServletMapper.update_duotiao_systemcode('2'); int delete_fail = yylfHttpServletMapper.delete('1'); //3-3更新失败:例如有外键,报异常 //int update_fail = yylfHttpServletMapper.update_fail('1'); //4查询 //4-1 没数:String 类型返回null //Object object = yylfHttpServletMapper.select('0'); //4-1 没数:集合 类型返回[]空集合 //Syncstatus syncstatus3 = new Syncstatus(); //syncstatus3.setStatus('7'); //List<Syncstatus> page0 = yylfHttpServletMapper.getSyncstatusList(syncstatus3); //4-1 没数:int 类型返回null,如果定义为int会报错。因为没数时返回null,可以将返回类型改为String //String i = yylfHttpServletMapper.select_int(0); //4-1:当返回值为对象时,若返回值为空,则返回null //4-2 有数 List<Syncstatus> pages = yylfHttpServletMapper.getSyncstatusList(vo); return new PageInfo<Syncstatus>(pages); }
2、对数据库的操作:
<update id='update_duotiao_systemcode'> UPDATE aaa SET systemcode = ’3’ WHERE systemcode = #{systemcode,jdbcType=VARCHAR} </update><delete id='delete'> delete from aaa where uuid = #{uuid,jdbcType=VARCHAR} </delete>
3、配置文件:
<?xml version='1.0' encoding='UTF-8'?><beans xmlns='http://www.springframework.org/schema/beans' xmlns:aop='http://www.springframework.org/schema/aop' xmlns:tx='http://www.springframework.org/schema/tx' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:context='http://www.springframework.org/schema/context' xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd'> <bean class='com.p6spy.engine.spy.P6DataSource'> <constructor-arg ref='dataSourceTarget'/> </bean> <!-- 定义使用dbcp2连接池的数据源 此处使用自定义的数据源,将用户名与密码解密处理 --> <bean > <property name='url' value='${jdbc.url}'> </property> <property name='username' value='${jdbc.username}'> </property> <property name='password' value='${jdbc.password}'> </property> <property name='driverClassName' value='${jdbc.driverClassName}'> </property> <!-- informix--> <!--<property name='validationQuery' value='select count(*) from systables'> </property>--> <!-- mysql检测方式 --> <property name='validationQuery' value='select 1'> </property> <!-- oracle检测方式 <property name='validationQuery' value='select 1 from dual'> </property> --> </bean> <!-- 配置SqlSessionFactoryBean --> <bean class='org.mybatis.spring.SqlSessionFactoryBean'> <!-- 注入数据源 相关信息看源码 --> <property name='dataSource' ref='dataSource' /> <!-- 扫描的实体所在的包--> <property name='configLocation' value='classpath:mybatis.xml'/> <!-- mapper和resultmap配置路径 --> <property name='mapperLocations'> <list><value>classpath:mybatis/*Mapper.xml</value> </list> </property> </bean> <!-- 自动扫描mapper接口,注入sqlsessionfactory --> <bean class='org.mybatis.spring.mapper.MapperScannerConfigurer'> <property name='basePackage' value='com.asd.modules.dao'/> </bean> <!-- 启用类扫描机制,通过元数据配置Service --> <context:component-scan base-package='com.asd'> <context:include-filter type='regex'expression='com.asd.modules.sevice.impl.*ServiceImpl' /> </context:component-scan> <!-- mybatis事物配置 --> <context:annotation-config /> <!-- ================================事务相关控制================================================= --> <bean class='org.springframework.jdbc.datasource.DataSourceTransactionManager'> <property name='dataSource' ref='dataSource' /> </bean> <tx:advice transaction-manager='transactionManager'> <tx:attributes> <tx:method name='delete*' propagation='REQUIRED' read-only='false' rollback-for='java.lang.Exception' no-rollback-for='java.lang.RuntimeException' /> <tx:method name='insert*' propagation='REQUIRED' read-only='false' rollback-for='java.lang.RuntimeException' /> <tx:method name='save*' propagation='REQUIRED' read-only='false' rollback-for='java.lang.RuntimeException' /> <tx:method name='update*' propagation='REQUIRED' read-only='false' rollback-for='java.lang.Exception' /> <tx:method name='find*' propagation='SUPPORTS' /> <tx:method name='get*' propagation='SUPPORTS' /> <tx:method name='select*' propagation='SUPPORTS' /> <tx:method name='*' propagation='REQUIRED' rollback-for='java.lang.Exception' /> </tx:attributes> </tx:advice> <aop:config> <!-- 把事务控制在Service层 --> <aop:pointcut expression='execution(* com.asd.modules.service.impl.*ServiceImpl.*(..))' /> <aop:advisor pointcut-ref='reinsPointCut' advice-ref='reinsAdvice' /> </aop:config></beans>
4、数据库语句:
-- 创建aaa表用来验证增删改查的返回值CREATE TABLE `reserve`.`aaa` ( `uuid` char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `systemcode` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `status` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`uuid`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- 创建bbb表用来关联aaa的uuid作外键CREATE TABLE `reserve`.`bbb` ( `uuid` char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `systemcode` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `status` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`uuid`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;alter table bbb add constraint FK_T_POSITI_REFERENCE_T_COMPAN foreign key (uuid)references aaa (uuid);insert into bbb (uuid,systemcode,status)value (’1’,’2’,’2’);-- 验证事支持DELETE from aaa where uuid != ’1’;insert into aaa (uuid,systemcode,status)value (’2’,’2’,’2’);SELECT * FROM aaa;
排查过程共查找了下述方面:
1、排除数据库原因:
查看mysql数据库是支持事务的;而且用informix数据库进行了验证,同样没有回滚。
2、验证了impl的类型等均为问题。
3、查看了事务的配置信息也正确好用。
4、验证了系统其它的一些方法,发现是支持事务的。
5、将这两个语句放到其它方法里也好用。
6、事务是在service层处理的,在控制层也加了异常捕获(这个操作并不会影响事务回滚,即使不catch,也会回滚的)
最终锁定问题原因:是因为方法名称的问题。
当将方法名改成其它的,不以get开头,不报错。
这个问题很坑,因为本以为为配置文件中的get*,会使这个方法的事务起作用,谁知道恰恰get*的这个配置虽然起作用了,但是结果却是事务不回滚,在将该配置改为
<tx:method name='get*' propagation='SUPPORTS' rollback-for='java.lang.Exception'/>
也没有用,最后将其注释掉,事务回滚。走了下面的配置:
<tx:method name='*' propagation='REQUIRED' rollback-for='java.lang.Exception' />
需要注意的是tx:method 的name属性指的是方法名。
将SUPPORTS改为REQUIRED后,事务也进行回滚。最终得到原因:是因为propagation的配置信息不正确。
拓展:
一、在声明式的事务处理中,要配置一个切面,其中就用到了propagation,表示打算对这些方法怎么使用事务,是用还是不用,其中propagation有七种配置,REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。默认是REQUIRED。
二、Spring中七种Propagation类的事务属性详解:
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。 REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。三、注意.
这个配置将影响数据存储,必须根据情况选择。
问题往往出现在你忽略的地方。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持好吧啦网。
相关文章:
1. CSS Hack大全-教你如何区分出IE6-IE10、FireFox、Chrome、Opera2. 《CSS3实战》笔记--渐变设计(一)3. CSS3实例分享之多重背景的实现(Multiple backgrounds)4. asp(vbs)Rs.Open和Conn.Execute的详解和区别及&H0001的说明5. chatGPT教我写compose函数的详细过程6. 在 XSL/XSLT 中实现随机排序7. JavaScript避免嵌套代码浅析8. XML在语音合成中的应用9. 用css截取字符的几种方法详解(css排版隐藏溢出文本)10. Vue Element UI 表单自定义校验规则及使用
