您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了如何解決Spring事務(wù)管理配置文件問(wèn)題,內(nèi)容簡(jiǎn)而易懂,希望大家可以學(xué)習(xí)一下,學(xué)習(xí)完之后肯定會(huì)有收獲的,下面讓小編帶大家一起來(lái)看看吧。
在開(kāi)發(fā)中,遇到了sql語(yǔ)句報(bào)錯(cuò),但是并沒(méi)有回滾的情況。
經(jīng)過(guò)幾天的排查,終于找到了事務(wù)沒(méi)有回滾的原因。
原來(lái)的項(xiàng)目用的是informix的數(shù)據(jù)庫(kù),原來(lái)針對(duì)事務(wù)回滾的機(jī)制都是好用的。我本地用的是mysql數(shù)據(jù)庫(kù)。
先將程序代碼與spring-mybatis.xml配置文件拿過(guò)來(lái):
1、程序代碼:
這個(gè)問(wèn)題是在驗(yàn)證增刪改查返回值時(shí)發(fā)現(xiàn)的。
兩個(gè)操作,刪除時(shí),因?yàn)殛P(guān)聯(lián)了外鍵,所以會(huì)報(bào)錯(cuò),此時(shí)正常情況更新的語(yǔ)句也會(huì)回滾,但是并沒(méi)有。
/** *@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ù),當(dāng)有一條數(shù)據(jù)錯(cuò)誤時(shí),所有數(shù)據(jù)都會(huì)插入失敗 int insert_success2 = yylfHttpServletMapper.insert_duotiao("7"); String insert_success3 = yylfHttpServletMapper.insert_duotiao_String("7");//不支持返回值為String類型 //1-3新增一條數(shù)據(jù):插入失?。褐麈I沖突,會(huì)直接報(bào)異常 int insert_failed = yylfHttpServletMapper.insert("1", "2", "1"); //1-4插入null:屬性為null,如果表中所有字段允許為null,插入一條所有值均為null的數(shù)據(jù) Syncstatus syncstatus1 = null; yylfHttpServletMapper.insertSyncstatus(syncstatus1); //1-5插入一個(gè)沒(méi)有賦值的對(duì)象:屬性為null,如果表中所有字段允許為null,插入一條所有值均為null的數(shù)據(jù) Syncstatus syncstatus2 = new Syncstatus(); yylfHttpServletMapper.insertSyncstatus(syncstatus2);*/ /*//2刪除:返回值自己定義,可以是void,int //2-1刪除成功:沒(méi)有數(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刪除失?。豪缬型怄I:報(bào)異常 //3更新:返回值自己定義,可以是void,int //3-1更新成功:沒(méi)有數(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,報(bào)異常 //int update_fail = yylfHttpServletMapper.update_fail("1"); //4查詢 //4-1 沒(méi)數(shù):String 類型返回null //Object object = yylfHttpServletMapper.select("0"); //4-1 沒(méi)數(shù):集合 類型返回[]空集合 //Syncstatus syncstatus3 = new Syncstatus(); //syncstatus3.setStatus("7"); //List<Syncstatus> page0 = yylfHttpServletMapper.getSyncstatusList(syncstatus3); //4-1 沒(méi)數(shù):int 類型返回null,如果定義為int會(huì)報(bào)錯(cuò)。因?yàn)闆](méi)數(shù)時(shí)返回null,可以將返回類型改為String //String i = yylfHttpServletMapper.select_int(0); //4-1:當(dāng)返回值為對(duì)象時(shí),若返回值為空,則返回null //4-2 有數(shù) List<Syncstatus> pages = yylfHttpServletMapper.getSyncstatusList(vo); return new PageInfo<Syncstatus>(pages); }
2、對(duì)數(shù)據(jù)庫(kù)的操作:
<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檢測(cè)方式 --> <property name="validationQuery" value="select 1"> </property> <!-- oracle檢測(cè)方式 <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" /> <!-- 掃描的實(shí)體所在的包--> <property name="configLocation" value="classpath:mybatis.xml"/> <!-- mapper和resultmap配置路徑 --> <property name="mapperLocations"> <list> <value>classpath:mybatis/*Mapper.xml</value> </list> </property> </bean> <!-- 自動(dòng)掃描mapper接口,注入sqlsessionfactory --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.asd.modules.dao"/> </bean> <!-- 啟用類掃描機(jī)制,通過(guò)元數(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ù)庫(kù)語(yǔ)句:
-- 創(chuàng)建aaa表用來(lái)驗(yàn)證增刪改查的返回值 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表用來(lái)關(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'); -- 驗(yàn)證事支持 DELETE from aaa where uuid != '1'; insert into aaa (uuid,systemcode,status)value ('2','2','2'); SELECT * FROM aaa;
排查過(guò)程共查找了下述方面:
1、排除數(shù)據(jù)庫(kù)原因:
查看mysql數(shù)據(jù)庫(kù)是支持事務(wù)的;而且用informix數(shù)據(jù)庫(kù)進(jìn)行了驗(yàn)證,同樣沒(méi)有回滾。
2、驗(yàn)證了impl的類型等均為問(wèn)題。
3、查看了事務(wù)的配置信息也正確好用。
4、驗(yàn)證了系統(tǒng)其它的一些方法,發(fā)現(xiàn)是支持事務(wù)的。
5、將這兩個(gè)語(yǔ)句放到其它方法里也好用。
6、事務(wù)是在service層處理的,在控制層也加了異常捕獲(這個(gè)操作并不會(huì)影響事務(wù)回滾,即使不catch,也會(huì)回滾的)
最終鎖定問(wèn)題原因:是因?yàn)榉椒Q的問(wèn)題。
當(dāng)將方法名改成其它的,不以get開(kāi)頭,不報(bào)錯(cuò)。
這個(gè)問(wèn)題很坑,因?yàn)楸疽詾闉榕渲梦募械膅et*,會(huì)使這個(gè)方法的事務(wù)起作用,誰(shuí)知道恰恰get*的這個(gè)配置雖然起作用了,但是結(jié)果卻是事務(wù)不回滾,在將該配置改為
<tx:method name="get*" propagation="SUPPORTS" rollback-for="java.lang.Exception"/>
也沒(méi)有用,最后將其注釋掉,事務(wù)回滾。走了下面的配置:
<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
需要注意的是tx:method 的name屬性指的是方法名。
將SUPPORTS改為REQUIRED后,事務(wù)也進(jìn)行回滾。最終得到原因:是因?yàn)閜ropagation的配置信息不正確。
拓展:
一、在聲明式的事務(wù)處理中,要配置一個(gè)切面,其中就用到了propagation,表示打算對(duì)這些方法怎么使用事務(wù),是用還是不用,其中propagation有七種配置,REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。默認(rèn)是REQUIRED。
二、Spring中七種Propagation類的事務(wù)屬性詳解:
三、注意.
這個(gè)配置將影響數(shù)據(jù)存儲(chǔ),必須根據(jù)情況選擇。
問(wèn)題往往出現(xiàn)在你忽略的地方。
以上就是關(guān)于如何解決Spring事務(wù)管理配置文件問(wèn)題的內(nèi)容,如果你們有學(xué)習(xí)到知識(shí)或者技能,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。