Spring事務(wù)管理配置文件問題排查
在開發(fā)中,遇到了sql語句報錯,但是并沒有回滾的情況。
經(jīng)過幾天的排查,終于找到了事務(wù)沒有回滾的原因。
原來的項目用的是informix的數(shù)據(jù)庫,原來針對事務(wù)回滾的機制都是好用的。我本地用的是mysql數(shù)據(jù)庫。
先將程序代碼與spring-mybatis.xml配置文件拿過來:
1、程序代碼:
這個問題是在驗證增刪改查返回值時發(fā)現(xiàn)的。
兩個操作,刪除時,因為關(guān)聯(lián)了外鍵,所以會報錯,此時正常情況更新的語句也會回滾,但是并沒有。
/**
*@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新增一條數(shù)據(jù):插入成功,返回值為1
int insert_success1 = yylfHttpServletMapper.insert("8", "2", "1");
//1-2新增多條數(shù)據(jù):插入成功,返回值為插入的數(shù)據(jù)條數(shù),當有一條數(shù)據(jù)錯誤時,所有數(shù)據(jù)都會插入失敗
int insert_success2 = yylfHttpServletMapper.insert_duotiao("7");
String insert_success3 = yylfHttpServletMapper.insert_duotiao_String("7");//不支持返回值為String類型
//1-3新增一條數(shù)據(jù):插入失?。褐麈I沖突,會直接報異常
int insert_failed = yylfHttpServletMapper.insert("1", "2", "1");
//1-4插入null:屬性為null,如果表中所有字段允許為null,插入一條所有值均為null的數(shù)據(jù)
Syncstatus syncstatus1 = null;
yylfHttpServletMapper.insertSyncstatus(syncstatus1);
//1-5插入一個沒有賦值的對象:屬性為null,如果表中所有字段允許為null,插入一條所有值均為null的數(shù)據(jù)
Syncstatus syncstatus2 = new Syncstatus();
yylfHttpServletMapper.insertSyncstatus(syncstatus2);*/
/*//2刪除:返回值自己定義,可以是void,int
//2-1刪除成功:沒有數(shù)據(jù):返回值為0
int delete_success1 = yylfHttpServletMapper.delete("0");
//2-2刪除成功:有多條數(shù)據(jù):返回值為刪除的數(shù)據(jù)條數(shù)
int delete_success2 = yylfHttpServletMapper.delete_systemcode("2");*/
//2-3刪除失敗:例如有外鍵:報異常
//3更新:返回值自己定義,可以是void,int
//3-1更新成功:沒有數(shù)據(jù),返回值為0
//int update_no = yylfHttpServletMapper.update_no("0");
//3-2更新成功:有多條數(shù)據(jù),返回更新的數(shù)據(jù)條數(shù)
int update_duotiao = yylfHttpServletMapper.update_duotiao_systemcode("2");
int delete_fail = yylfHttpServletMapper.delete("1");
//3-3更新失?。豪缬型怄I,報異常
//int update_fail = yylfHttpServletMapper.update_fail("1");
//4查詢
//4-1 沒數(shù):String 類型返回null
//Object object = yylfHttpServletMapper.select("0");
//4-1 沒數(shù):集合 類型返回[]空集合
//Syncstatus syncstatus3 = new Syncstatus();
//syncstatus3.setStatus("7");
//List<Syncstatus> page0 = yylfHttpServletMapper.getSyncstatusList(syncstatus3);
//4-1 沒數(shù):int 類型返回null,如果定義為int會報錯。因為沒數(shù)時返回null,可以將返回類型改為String
//String i = yylfHttpServletMapper.select_int(0);
//4-1:當返回值為對象時,若返回值為空,則返回null
//4-2 有數(shù)
List<Syncstatus> pages = yylfHttpServletMapper.getSyncstatusList(vo);
return new PageInfo<Syncstatus>(pages);
}
2、對數(shù)據(jù)庫的操作:
<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 id="dataSource" class="com.p6spy.engine.spy.P6DataSource">
<constructor-arg ref="dataSourceTarget"/>
</bean>
<!-- 定義使用dbcp2連接池的數(shù)據(jù)源
此處使用自定義的數(shù)據(jù)源,將用戶名與密碼解密處理
-->
<bean id="dataSourceTarget" class="com.asd.common.jdbc.MyBasicDataSource" >
<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 id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入數(shù)據(jù)源 相關(guān)信息看源碼 -->
<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>
<!-- 啟用類掃描機制,通過元數(shù)據(jù)配置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 />
<!-- ================================事務(wù)相關(guān)控制================================================= -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="reinsAdvice" 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>
<!-- 把事務(wù)控制在Service層 -->
<aop:pointcut id="reinsPointCut"
expression="execution(* com.asd.modules.service.impl.*ServiceImpl.*(..))" />
<aop:advisor pointcut-ref="reinsPointCut"
advice-ref="reinsAdvice" />
</aop:config>
</beans>
4、數(shù)據(jù)庫語句:
-- 創(chuàng)建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;
-- 創(chuàng)建bbb表用來關(guān)聯(lián)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、排除數(shù)據(jù)庫原因:

查看mysql數(shù)據(jù)庫是支持事務(wù)的;而且用informix數(shù)據(jù)庫進行了驗證,同樣沒有回滾。
2、驗證了impl的類型等均為問題。
3、查看了事務(wù)的配置信息也正確好用。
4、驗證了系統(tǒng)其它的一些方法,發(fā)現(xiàn)是支持事務(wù)的。
5、將這兩個語句放到其它方法里也好用。
6、事務(wù)是在service層處理的,在控制層也加了異常捕獲(這個操作并不會影響事務(wù)回滾,即使不catch,也會回滾的)
最終鎖定問題原因:是因為方法名稱的問題。
當將方法名改成其它的,不以get開頭,不報錯。
這個問題很坑,因為本以為為配置文件中的get*,會使這個方法的事務(wù)起作用,誰知道恰恰get*的這個配置雖然起作用了,但是結(jié)果卻是事務(wù)不回滾,在將該配置改為
<tx:method name="get*" propagation="SUPPORTS" rollback-for="java.lang.Exception"/>
也沒有用,最后將其注釋掉,事務(wù)回滾。走了下面的配置:
<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
需要注意的是tx:method 的name屬性指的是方法名。
將SUPPORTS改為REQUIRED后,事務(wù)也進行回滾。最終得到原因:是因為propagation的配置信息不正確。
拓展:
一、在聲明式的事務(wù)處理中,要配置一個切面,其中就用到了propagation,表示打算對這些方法怎么使用事務(wù),是用還是不用,其中propagation有七種配置,REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。默認是REQUIRED。
二、Spring中七種Propagation類的事務(wù)屬性詳解:
- REQUIRED:支持當前事務(wù),如果當前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇。
- SUPPORTS:支持當前事務(wù),如果當前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
- MANDATORY:支持當前事務(wù),如果當前沒有事務(wù),就拋出異常。
- REQUIRES_NEW:新建事務(wù),如果當前存在事務(wù),把當前事務(wù)掛起。
- NOT_SUPPORTED:以非事務(wù)方式執(zhí)行操作,如果當前存在事務(wù),就把當前事務(wù)掛起。
- NEVER:以非事務(wù)方式執(zhí)行,如果當前存在事務(wù),則拋出異常。
- NESTED:支持當前事務(wù),如果當前事務(wù)存在,則執(zhí)行一個嵌套事務(wù),如果當前沒有事務(wù),就新建一個事務(wù)。
三、注意.
這個配置將影響數(shù)據(jù)存儲,必須根據(jù)情況選擇。
問題往往出現(xiàn)在你忽略的地方。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java 矩陣乘法的mapreduce程序?qū)崿F(xiàn)
這篇文章主要介紹了java 矩陣乘法的mapreduce程序?qū)崿F(xiàn)的相關(guān)資料,需要的朋友可以參考下2017-06-06
???????Spring多租戶數(shù)據(jù)源管理 AbstractRoutingDataSource
本文技術(shù)了???????Spring多租戶數(shù)據(jù)源管理 AbstractRoutingDataSource,下文詳細內(nèi)容介紹,需要的小伙伴可以參考一下2022-05-05
springboot mybatis druid配置多數(shù)據(jù)源教程
這篇文章主要介紹了springboot mybatis druid配置多數(shù)據(jù)源教程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Spring Boot監(jiān)聽Redis Key失效事件實現(xiàn)定時任務(wù)的示例
這篇文章主要介紹了Spring Boot監(jiān)聽Redis Key失效事件實現(xiàn)定時任務(wù)的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04
Spring Boot使用Druid和監(jiān)控配置方法
Druid是Java語言中最好的數(shù)據(jù)庫連接池,并且能夠提供強大的監(jiān)控和擴展功能。下面來說明如何在 Spring Boot 中配置使用Druid2017-04-04
Springboot如何實現(xiàn)自定義異常數(shù)據(jù)
這篇文章主要介紹了Springboot如何實現(xiàn)自定義異常數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09

