前言:
? ? Spring的事務纫骑,也就是數(shù)據(jù)庫的事務操作蒙袍,符合ACID標準,也具有標準的事務隔離級別。
? ? 但是Spring事務有自己的特點细移,也就是事務傳播機制澈吨。
? ??所謂事務傳播機制宛蚓,也就是在事務在多個方法的調用中是如何傳遞的斜姥,是重新創(chuàng)建事務還是使用父方法的事務?父方法的回滾對子方法的事務是否有影響武花?這些都是可以通過事務傳播機制來決定的圆凰。
? ? 本文就測試一下這些事務傳播機制的使用及異同
1.準備測試方法
? ? 主要是創(chuàng)建兩個service接口(接口主要是對數(shù)據(jù)庫表的操作),并創(chuàng)建其實現(xiàn)類
1)創(chuàng)建beans.xml体箕,開啟事務
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
? ? ? ? http://www.springframework.org/schema/beans
? ? ? ? http://www.springframework.org/schema/beans/spring-beans.xsd
? ? ? ? http://www.springframework.org/schema/tx
? ? ? ? http://www.springframework.org/schema/tx/spring-tx.xsd
? ? ? ? http://www.springframework.org/schema/aop
? ? ? ? http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
?? 2)創(chuàng)建實體類和表(表創(chuàng)建讀者可自定義創(chuàng)建)
@Data
@AllArgsConstructor
@NoArgsConstructor
publicclassBlog{
privateintid;
privateString name;
privateString ur;
}
? ? 3)創(chuàng)建service接口(BlogService和BlogService2专钉,主要是對Blog的不同操作)
// BlogService
packagejdbc;
// 主要負責Blog的添加和修改
publicinterfaceBlogService{
voidsave(Blog blog);
voidupdate(Blog blog);
}
// BlogService2
packagejdbc;
// 主要負責Blog的刪除
publicinterfaceBlogService2{
voiddelete(intid);
}
?4)創(chuàng)建其實現(xiàn)類(BlogServiceImpl,BlogService2)
BlogServiceImpl.java
@Transactional(propagation=Propagation.REQUIRED)
@Component
publicclassBlogServiceImplimplementsBlogService{
@Autowired
privateJdbcTemplate jdbcTemplate;
@Autowired
privateBlogService2 blogService2;
@Override
publicvoidsave(Blog blog){
String sql ="insert into blog values(?,?,?)";
jdbcTemplate.update(sql,
newObject[]{blog.getId(),blog.getName(),blog.getUr()},
newint[]{java.sql.Types.INTEGER,java.sql.Types.VARCHAR,java.sql.Types.VARCHAR});
blogService2.delete(16);
// update(blog);
// throw new RuntimeException("error");
}
@Override
publicvoidupdate(Blog blog){
String sql ="update blog set name = ? where id=?";
jdbcTemplate.update(sql,newObject[]{blog.getName(),blog.getId()},
newint[]{java.sql.Types.VARCHAR,java.sql.Types.INTEGER});
}
}
BlogService2.java
@Transactional(propagation=Propagation.REQUIRED)
@Component
publicclassBlogServiceImpl2implementsBlogService2{
@Autowired
privateJdbcTemplate jdbcTemplate;
@Override
publicvoiddelete(intid){
String sql ="delete from blog where id=?";
jdbcTemplate.update(sql, id);
}
}
? ? 注意:既然要實現(xiàn)多事務的傳播累铅,就需要在一個方法里調用另一個類的方法跃须,下面的測試就是基于這種方法,在BlogService的save()方法中調用BlogService2的delete()方法
? ? 5)創(chuàng)建Configuration類娃兽,用于創(chuàng)建DataSource實現(xiàn)
@Configuration
@ComponentScan(basePackages={"jdbc"})// 掃描BlogService實現(xiàn)類所在的包路徑
@ImportResource(locations={"classpath:beans.xml"})// 添加事務管理
publicclassJdbcConfig{
@Bean
publicJdbcTemplatejdbcTemplate(DataSource dataSource){
returnnewJdbcTemplate(dataSource);
}
@Bean
publicDataSourceTransactionManagertransactionManager(DataSource dataSource){
returnnewDataSourceTransactionManager(dataSource);
}
@Bean
publicDataSourcedataSource(){
try{
returnnewSimpleDriverDataSource(newcom.mysql.jdbc.Driver(),"jdbc:mysql://localhost:3306/test","root","root");
}catch(SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
returnnull;
}
6)測試
publicclassTest{
publicstaticvoidmain(String[] args){
AnnotationConfigApplicationContext ac =newAnnotationConfigApplicationContext(JdbcConfig.class);
BlogService service = ac.getBean(BlogService.class);
Blog b =newBlog(18,"lili","url");
service.save(b);
}
}
總結:大體的測試框架就如上所示菇民,下面的測試修改主要是修改BlogServiceImpl和BlogServiceImpl2的事務傳播機制@Transactional(propagation=Propagation.REQUIRED)
3.事務傳播機制的測試
? ? 1)REQUIRED
定義:如果有事務則加入事務,如果沒有事務投储,則創(chuàng)建一個新的(默認值)
操作1:將BlogServiceImpl和BlogServiceImpl2的事務傳播機制都修改為
@Transactional(propagation=Propagation.REQUIRED)
?結果1:
操作2:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED)第练,BlogServiceImpl2的仍為@Transactional(propagation=Propagation.REQUIRED)
結果2:
總結:
? ??? ??當BlogServiceImpl提供事務的時候,BlogServiceImpl2的方法執(zhí)行使用當前已有事務玛荞,不再新建事務娇掏;
如果出現(xiàn)異常則全部回滾;
? ? ? ? 當BlogServiceImpl不創(chuàng)建事務的時候勋眯,BlogServiceImpl2的方法執(zhí)行發(fā)現(xiàn)沒有事務可用婴梧,自己新建事務壁涎;
2)NOT_SUPPORTED
定義:Spring不為當前方法開啟事務,相當于沒有事務
? ? 操作:將BlogServiceImpl和BlogServiceImpl2的事務傳播機制都
修改為@Transactional(propagation=Propagation.NOT_SUPPORTED)
結果:
總結:
? ? ? ? NOT_SUPPORTED相當于沒有Spring事務志秃,每條執(zhí)行語句單獨執(zhí)行,單獨提交
3)REQUIRES_NEW
定義:不管是否存在事務嚼酝,都創(chuàng)建一個新的事務浮还,原來的方法掛起,新的方法執(zhí)行完畢后闽巩,繼續(xù)執(zhí)行老的事務
操作:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.REQUIRED)钧舌,BlogServiceImpl2的仍為@Transactional(propagation=Propagation.REQUIRES_NEW)
結果:
? ? 總結:
? ? ? ? REQUIRES_NEW為當前方法創(chuàng)建一個新的事務,并且當前事務先提交涎跨,然后再提交老的事務
4)MANDATORY
定義:必須在一個已有的事務中執(zhí)行洼冻,否則報錯
操作:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.MANDATORY)隅很,查看是否報錯
? 結果:
? ? 總結:? ??
? ? ? ? MANDATORY必須在已有事務下被調用撞牢,否則報錯
? ? ? ? NOT_SUPPORTED執(zhí)行數(shù)據(jù)庫層面的事務操作,故當前測試中叔营,insert方法成功執(zhí)行屋彪,delete方法的拋錯并不影響insert方法的執(zhí)行
5)NEVER
? ? 定義:必須在一個沒有的事務中執(zhí)行,否則報錯
操作:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.REQUIRED)绒尊,BlogServiceImpl2的仍為@Transactional(propagation=Propagation.MANDATORY)畜挥,查看是否報錯
? 結果:
? ? 總結:
? ? ? ? NEVER必須在沒有事務的方法中執(zhí)行,否則報錯婴谱;
? ? ? ? save方法開啟一個事務蟹但,還沒來及提交發(fā)現(xiàn)delete方法報錯,只能回滾事務
? 6)SUPPORTS
定義:如果其他bean調用這個方法時谭羔,其他bean聲明了事務华糖,則就用這個事務,如果沒有聲明事務口糕,那就不用事務
操作1:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.REQUIRED)缅阳,BlogServiceImpl2的仍為@Transactional(propagation=Propagation.SUPPORTS)
結果1:
操作1:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.SUPPORTS)
結果1:
? ??總結:
? ? ? ? SUPPORTS類型的事務傳播機制景描,是否使用事務取決于調用方法是否有事務十办,如果有則直接用,如果沒有則不使用事務
7)NESTED
定義:如果當前存在事務超棺,則在嵌套事務內執(zhí)行向族。如果當前沒有事務,則執(zhí)行與REQUIRED類似的操作
操作1:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.REQUIRED)棠绘,BlogServiceImpl2的仍為@Transactional(propagation=Propagation.NESTED)
?結果1:
操作2:將BlogServiceImpl事務傳播機制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED)件相,BlogServiceImpl2的仍為@Transactional(propagation=Propagation.NESTED)
?結果2:
? ? 總結:
? ? ? ? save方法創(chuàng)建一個事務再扭,則再調用delete方法時,直接在該事務的基礎上創(chuàng)建一個嵌套事務夜矗,本質上還是同一個事務泛范,做一次提交;
? ? ? ? save方法不創(chuàng)建事務紊撕,則調用delete方法時罢荡,直接創(chuàng)建一個新的事務,單獨提交
4.注意事項
1)REQUIRED
? ? ? ? 當兩個方法的傳播機制都是REQUIRED時对扶,如果一旦發(fā)生回滾区赵,兩個方法都會回滾
? 2)REQUIRES_NEW
? ? ? ? 當delete方法傳播機制為REQUIRES_NEW,會開啟一個新的事務浪南,并單獨提交方法笼才,所以save方法的回滾并不影響delete方法事務提交
? 3)NESTED
? ? ? ? 當save方法為REQUIRED,delete方法為NESTED時络凿,delete方法開啟一個嵌套事務骡送;
? ? ? ? 當save方法回滾時,delete方法也會回滾絮记;反之各谚,如果delete方法回滾,則并不影響save方法的提交