一文解析spring中事務(wù)的傳播機(jī)制
本文講解"一文解析spring中事務(wù)的傳播機(jī)制",希望能夠解決相關(guān)問(wèn)題。
spring中的事務(wù)
spring的事務(wù)其實(shí)就是數(shù)據(jù)庫(kù)的事務(wù)操作,符合acid標(biāo)準(zhǔn),也具有標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別。
spring中的事務(wù)只是對(duì)jdbc事務(wù)進(jìn)行一些封裝與擴(kuò)展,其底層最終還是會(huì)使用到j(luò)dbc的這套api。但是spring事務(wù)有自己的特點(diǎn),也就是事務(wù)傳播機(jī)制。
所謂事務(wù)傳播機(jī)制,也就是在事務(wù)在多個(gè)方法的調(diào)用中是如何傳遞的,是重新創(chuàng)建事務(wù)還是使用父方法的事務(wù)?父方法的回滾對(duì)子方法的事務(wù)是否有影響?這些都是可以通過(guò)事務(wù)傳播機(jī)制來(lái)決定的。
準(zhǔn)備工作
實(shí)體類area
package com.morris.spring.entity;import lombok.data;import java.io.serializable;@datapublic class area implements serializable {private integer id;private string areaname;private integer areacode;}
good
package com.morris.spring.entity;import lombok.data;import java.io.serializable;import java.math.bigdecimal;@datapublic class good implements serializable {private integer id;private string goodname;private bigdecimal price;}
dao層areadao
package com.morris.spring.dao;import com.morris.spring.entity.area;import org.springframework.beans.factory.annotation.autowired;import org.springframework.jdbc.core.jdbctemplate;public class areadao {@autowiredprivate jdbctemplate jdbctemplate;public boolean insert(area area) {string sql = "insert into t_area(area_name, area_code) values(?,?)";return jdbctemplate.update(sql, area.getareaname(), area.getareacode()) > 0;}}
gooddao
package com.morris.spring.dao;import com.morris.spring.entity.good;import org.springframework.beans.factory.annotation.autowired;import org.springframework.jdbc.core.jdbctemplate;public class gooddao {@autowiredprivate jdbctemplate jdbctemplate;public boolean insert(good good) {string sql = "insert into t_good(good_name, price) values(?,?)";return jdbctemplate.update(sql, good.getgoodname(), good.getprice()) > 0;}}
service層areaserviceimpl
package com.morris.spring.service;import com.morris.spring.dao.areadao;import com.morris.spring.entity.area;import org.springframework.beans.factory.annotation.autowired;import org.springframework.transaction.annotation.propagation;import org.springframework.transaction.annotation.transactional;public class areaserviceimpl implements areaservice {@autowiredprivate areadao areadao;@transactional(propagation = propagation.required)@overridepublic boolean addarea(int i) {int y = 1000000 / i;area area = new area();area.setareacode(y);area.setareaname("shenzhen");return areadao.insert(area);}}
goodserviceimpl
package com.morris.spring.service;import com.morris.spring.dao.gooddao;import com.morris.spring.entity.good;import org.springframework.beans.factory.annotation.autowired;import org.springframework.transaction.annotation.propagation;import org.springframework.transaction.annotation.transactional;import java.math.bigdecimal;public class goodserviceimpl implements goodservice {@autowiredprivate gooddao gooddao;@transactional(propagation = propagation.required)@overridepublic boolean addgood() {good good = new good();good.setgoodname("iphone");good.setprice(bigdecimal.valueof(99999));return gooddao.insert(good);}}
transactionservice
package com.morris.spring.service;import org.springframework.beans.factory.annotation.autowired;import org.springframework.stereotype.component;import org.springframework.transaction.annotation.propagation;import org.springframework.transaction.annotation.transactional;@componentpublic class transactionservice {@autowiredprivate goodservice goodservice;@autowiredprivate areaservice areaservice;@transactional(propagation = propagation.required)public void addgoodandarea() {system.out.println("------addgoodandarea-------");areaservice.addarea(10);goodservice.addgood();}}測(cè)試類
transactionpropagationdemo
package com.morris.spring.demo.jdbc;import com.morris.spring.config.jdbcconfig;import com.morris.spring.dao.areadao;import com.morris.spring.dao.gooddao;import com.morris.spring.service.areaserviceimpl;import com.morris.spring.service.goodserviceimpl;import com.morris.spring.service.transactionservice;import org.junit.jupiter.api.test;import org.springframework.context.annotation.annotationconfigapplicationcontext;/** * 事務(wù)的傳播機(jī)制 */public class transactionpropagationdemo {@testpublic void test() {annotationconfigapplicationcontext applicationcontext = new annotationconfigapplicationcontext();applicationcontext.register(gooddao.class);applicationcontext.register(areadao.class);applicationcontext.register(goodserviceimpl.class);applicationcontext.register(areaserviceimpl.class);applicationcontext.register(jdbcconfig.class);applicationcontext.register(transactionservice.class);applicationcontext.refresh();transactionservice transactionservice = applicationcontext.getbean(transactionservice.class);transactionservice.addgoodandarea();}}
傳播機(jī)制
具體選項(xiàng)可以參考枚舉類org.springframework.transaction.annotation.propagation。
選項(xiàng) | 說(shuō)明 |
required | 默認(rèn)選項(xiàng)。如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù),如果已經(jīng)存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中。 |
supports | 支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行。 |
mandatory | 使用當(dāng)前的事務(wù),如果當(dāng)前沒(méi)有事務(wù),就拋出異常。 |
requires_new | 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。 |
not_supported | 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。 |
never | 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。 |
nested | 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行與required類似的操作。 |
默認(rèn)選項(xiàng)。如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù),如果已經(jīng)存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中。
配置如下:
- transactionservice:required
- areaserviceimpl:required
- goodserviceimpl:required
運(yùn)行上面的demo,運(yùn)行結(jié)果如下:
// 創(chuàng)建第一個(gè)事務(wù) debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 創(chuàng)建第一個(gè)連接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- // 在第一個(gè)事務(wù)中執(zhí)行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 在第一個(gè)事務(wù)中執(zhí)行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第一個(gè)事務(wù) debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
總結(jié):
- 當(dāng)外層沒(méi)有事務(wù)的時(shí)候,transactionservice.addgoodandarea()方法執(zhí)行發(fā)現(xiàn)沒(méi)有事務(wù)可用,自己新建事務(wù)。
- goodservice.addgood()和areaservice.addarea()執(zhí)行時(shí)發(fā)現(xiàn)已有事務(wù),就使用當(dāng)前事務(wù)執(zhí)行。
新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
配置如下:
- transactionservice:required
- goodserviceimpl:requires
- areaserviceimpl:requires_new
運(yùn)行結(jié)果如下:
// 創(chuàng)建第一個(gè)事務(wù) debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 創(chuàng)建第一個(gè)連接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] to manual commit ------addgoodandarea------- // 掛起第一個(gè)事務(wù)并創(chuàng)建第二個(gè)事務(wù) debug datasourcetransactionmanager:446 - suspending current transaction, creating new transaction with name [com.morris.spring.service.areaserviceimpl.addarea] // 創(chuàng)建第二個(gè)連接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@116fc68] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@116fc68] to manual commit debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第二個(gè)事務(wù) debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@116fc68] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@116fc68] after transaction // 恢復(fù)第一個(gè)事務(wù) debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // 在第一個(gè)事務(wù)中執(zhí)行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] after transaction
總結(jié):areaserviceimpl.addarea()執(zhí)行時(shí)發(fā)現(xiàn)已有事務(wù),就把當(dāng)前事務(wù)掛起,執(zhí)行完后再恢復(fù)。
supports支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行。
配置如下:
- transactionservice:supports
- areaserviceimpl:required
- goodserviceimpl:supports
運(yùn)行結(jié)果如下:
// transactionservice.addgoodandarea以非事務(wù)方式運(yùn)行 ------addgoodandarea------- // 開啟第一個(gè)事務(wù) debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.areaserviceimpl.addarea]: propagation_required,isolation_default // 創(chuàng)建第一個(gè)連接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] to manual commit debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第一個(gè)事務(wù) debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] after transaction debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // areaserviceimpl.addarea以非事務(wù)方式運(yùn)行 debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)]
總結(jié):當(dāng)前沒(méi)有事務(wù),transactionservice.addgoodandarea()和areaserviceimpl.addarea()以非事務(wù)方式運(yùn)行。
修改配置如下:
- transactionservice:required
- areaserviceimpl:supports
- goodserviceimpl:supports
運(yùn)行結(jié)果如下:
// 開啟第一個(gè)事務(wù) debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 創(chuàng)建第一個(gè)連接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@11158fb] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@11158fb] to manual commit ------addgoodandarea------- // 在第一個(gè)事務(wù)中執(zhí)行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 在第一個(gè)事務(wù)中執(zhí)行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 提交第一個(gè)事務(wù) debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@11158fb] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@11158fb] after transaction
總結(jié):當(dāng)前有事務(wù),goodserviceimpl.addgood()和areaserviceimpl.addarea()以事務(wù)方式運(yùn)行。
mandatory使用當(dāng)前的事務(wù),如果當(dāng)前沒(méi)有事務(wù),就拋出異常。
配置如下:
- transactionservice:required
- areaserviceimpl:required
- goodserviceimpl:mandatory
運(yùn)行結(jié)果如下:
// 創(chuàng)建第一個(gè)事務(wù) ebug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 創(chuàng)建第一個(gè)連接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] to manual commit ------addgoodandarea------- // 在第一個(gè)事務(wù)中執(zhí)行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 在第一個(gè)事務(wù)中執(zhí)行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 提交第一個(gè)事務(wù) debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] after transaction
總結(jié):當(dāng)前有事務(wù),goodserviceimpl.addgood()以事務(wù)方式運(yùn)行。
修改配置如下:
- transactionservice:supports
- areaserviceimpl:mandatory
- goodserviceimpl:supports
運(yùn)行結(jié)果如下:
------addgoodandarea------- debug datasourcetransactionmanager:888 - should roll back transaction but cannot - no transaction available org.springframework.transaction.illegaltransactionstateexception: no existing transaction found for transaction marked with propagation 'mandatory' at org.springframework.transaction.support.abstractplatformtransactionmanager.gettransaction(abstractplatformtransactionmanager.java:372) at org.springframework.transaction.interceptor.transactionaspectsupport.createtransactionifnecessary(transactionaspectsupport.java:595) at org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:374) at org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:118) at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:205) at org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:219) at com.sun.proxy.$proxy23.addarea(unknown source) at com.morris.spring.service.transactionservice.addgoodandarea(transactionservice.java:20)
總結(jié):當(dāng)前沒(méi)有有事務(wù),areaserviceimpl.addarea()會(huì)拋出異常。
not_supported以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
配置如下:
- transactionservice:required
- areaserviceimpl:not_supported
- goodserviceimpl:not_supported
運(yùn)行結(jié)果如下:
// 開啟第一個(gè)事務(wù) debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- // 掛起第一個(gè)事務(wù) debug datasourcetransactionmanager:436 - suspending current transaction // 以非事務(wù)方式運(yùn)行 debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] debug datasourceutils:115 - fetching jdbc connection from datasource debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] // 恢復(fù)第一個(gè)事務(wù) debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // 掛起第一個(gè)事務(wù) debug datasourcetransactionmanager:436 - suspending current transaction // 以非事務(wù)方式運(yùn)行 debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourceutils:115 - fetching jdbc connection from datasource debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] // 恢復(fù)第一個(gè)事務(wù) debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // 提交第一個(gè)事務(wù) debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
總結(jié):執(zhí)行g(shù)oodserviceimpl.addgood()和areaserviceimpl.addarea()時(shí)當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
never以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
配置如下:
- transactionservice:never
- areaserviceimpl:never
- goodserviceimpl:never
運(yùn)行結(jié)果如下:
// 沒(méi)有事務(wù)都以非事務(wù)方式運(yùn)行 ------addgoodandarea------- debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] debug datasourceutils:115 - fetching jdbc connection from datasource debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)]
總結(jié):當(dāng)前沒(méi)有事務(wù)都以非事務(wù)方式執(zhí)行。
修改配置如下:
- transactionservice:required
- areaserviceimpl:never
- goodserviceimpl:never
運(yùn)行結(jié)果如下:
// 開啟第一個(gè)事務(wù) debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- debug datasourcetransactionmanager:864 - initiating transaction rollback debug datasourcetransactionmanager:345 - rolling back jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction org.springframework.transaction.illegaltransactionstateexception: existing transaction found for transaction marked with propagation 'never' at org.springframework.transaction.support.abstractplatformtransactionmanager.handleexistingtransaction(abstractplatformtransactionmanager.java:430) at org.springframework.transaction.support.abstractplatformtransactionmanager.gettransaction(abstractplatformtransactionmanager.java:362) at org.springframework.transaction.interceptor.transactionaspectsupport.createtransactionifnecessary(transactionaspectsupport.java:595) at org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:374) at org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:118) at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:205) at org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:219) at com.sun.proxy.$proxy23.addarea(unknown source) at com.morris.spring.service.transactionservice.addgoodandarea(transactionservice.java:20)
總結(jié):areaserviceimpl.addarea()執(zhí)行時(shí)存在事務(wù)就會(huì)拋出異常。
nested如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行與required類似的操作。
配置如下:
- transactionservice:required
- goodserviceimpl:nested
- areaserviceimpl:nested
運(yùn)行結(jié)果如下:
// 創(chuàng)建第一個(gè)事務(wù) ebug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 創(chuàng)建第一個(gè)連接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- // 創(chuàng)建回滾點(diǎn) debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.areaserviceimpl.addarea] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 釋放回滾點(diǎn) debug datasourcetransactionmanager:754 - releasing transaction savepoint // 創(chuàng)建回滾點(diǎn) debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.goodserviceimpl.addgood] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 釋放回滾點(diǎn) debug datasourcetransactionmanager:754 - releasing transaction savepoint debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
如果不拋出異常,使用required與nested都差不多,區(qū)別在于發(fā)生異常,下面演示required與nested發(fā)生異常時(shí)的區(qū)別:
配置如下:
- transactionservice:required
- areaserviceimpl:required
- goodserviceimpl:required
transactionservice.addgoodandarea修改如下:
@transactional(propagation = propagation.required) public void addgoodandarea() { system.out.println("------addgoodandarea-------"); try { areaservice.addarea(0); } catch (exception e) { e.printstacktrace(); } goodservice.addgood(); }
運(yùn)行結(jié)果如下:
debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] to manual commit ------addgoodandarea------- debug datasourcetransactionmanager:487 - participating in existing transaction debug datasourcetransactionmanager:877 - participating transaction failed - marking existing transaction as rollback-only debug datasourcetransactionmanager:360 - setting jdbc transaction [com.mysql.cj.jdbc.connectionimpl@f9aa66] rollback-only java.lang.arithmeticexception: / by zero at com.morris.spring.service.areaserviceimpl.addarea(areaserviceimpl.java:18) ... ... debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourcetransactionmanager:723 - global transaction is marked as rollback-only but transactional code requested commit debug datasourcetransactionmanager:877 - participating transaction failed - marking existing transaction as rollback-only debug datasourcetransactionmanager:360 - setting jdbc transaction [com.mysql.cj.jdbc.connectionimpl@f9aa66] rollback-only debug datasourcetransactionmanager:723 - global transaction is marked as rollback-only but transactional code requested commit debug datasourcetransactionmanager:864 - initiating transaction rollback debug datasourcetransactionmanager:345 - rolling back jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] after transaction org.springframework.transaction.unexpectedrollbackexception: transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.abstractplatformtransactionmanager.processrollback(abstractplatformtransactionmanager.java:905) ... ...
從運(yùn)行結(jié)果可以發(fā)現(xiàn)事務(wù)全部都回滾了。
將上面的配置修改如下:
- transactionservice:required
- areaserviceimpl:nested
- goodserviceimpl:nested
運(yùn)行結(jié)果如下:
debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.areaserviceimpl.addarea] debug datasourcetransactionmanager:857 - rolling back transaction to savepoint java.lang.arithmeticexception: / by zero at com.morris.spring.service.areaserviceimpl.addarea(areaserviceimpl.java:18) ... ... debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.goodserviceimpl.addgood] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourcetransactionmanager:754 - releasing transaction savepoint debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
從運(yùn)行結(jié)果可以發(fā)現(xiàn)areaservice.addarea()回滾了(本來(lái)就沒(méi)有提交內(nèi)容),goodservice.addgood()的內(nèi)容提交了。
注意只有運(yùn)行時(shí)異常以及rollbakcfor指定的異常才會(huì)回滾。