前言:
? ? Spring的事務(wù),也就是數(shù)據(jù)庫(kù)的事務(wù)操作,符合ACID標(biāo)準(zhǔn),也具有標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別。
? ? 但是Spring事務(wù)有自己的特點(diǎn),也就是事務(wù)傳播機(jī)制。
? ??所謂事務(wù)傳播機(jī)制,也就是在事務(wù)在多個(gè)方法的調(diào)用中是如何傳遞的,是重新創(chuàng)建事務(wù)還是使用父方法的事務(wù)?父方法的回滾對(duì)子方法的事務(wù)是否有影響?這些都是可以通過(guò)事務(wù)傳播機(jī)制來(lái)決定的。
? ? 本文就測(cè)試一下這些事務(wù)傳播機(jī)制的使用及異同
1.準(zhǔn)備測(cè)試方法
? ? 主要是創(chuàng)建兩個(gè)service接口(接口主要是對(duì)數(shù)據(jù)庫(kù)表的操作),并創(chuàng)建其實(shí)現(xiàn)類
1)創(chuàng)建beans.xml,開(kāi)啟事務(wù)
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)建實(shí)體類和表(表創(chuàng)建讀者可自定義創(chuàng)建)
@Data
@AllArgsConstructor
@NoArgsConstructor
publicclassBlog{
privateintid;
privateString name;
privateString ur;
}
? ? 3)創(chuàng)建service接口(BlogService和BlogService2,主要是對(duì)Blog的不同操作)
// BlogService
packagejdbc;
// 主要負(fù)責(zé)Blog的添加和修改
publicinterfaceBlogService{
voidsave(Blog blog);
voidupdate(Blog blog);
}
// BlogService2
packagejdbc;
// 主要負(fù)責(zé)Blog的刪除
publicinterfaceBlogService2{
voiddelete(intid);
}
?4)創(chuàng)建其實(shí)現(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);
}
}
? ? 注意:既然要實(shí)現(xiàn)多事務(wù)的傳播,就需要在一個(gè)方法里調(diào)用另一個(gè)類的方法,下面的測(cè)試就是基于這種方法,在BlogService的save()方法中調(diào)用BlogService2的delete()方法
? ? 5)創(chuàng)建Configuration類,用于創(chuàng)建DataSource實(shí)現(xiàn)
@Configuration
@ComponentScan(basePackages={"jdbc"})// 掃描BlogService實(shí)現(xiàn)類所在的包路徑
@ImportResource(locations={"classpath:beans.xml"})// 添加事務(wù)管理
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)測(cè)試
publicclassTest{
publicstaticvoidmain(String[] args){
AnnotationConfigApplicationContext ac =newAnnotationConfigApplicationContext(JdbcConfig.class);
BlogService service = ac.getBean(BlogService.class);
Blog b =newBlog(18,"lili","url");
service.save(b);
}
}
總結(jié):大體的測(cè)試框架就如上所示,下面的測(cè)試修改主要是修改BlogServiceImpl和BlogServiceImpl2的事務(wù)傳播機(jī)制@Transactional(propagation=Propagation.REQUIRED)
3.事務(wù)傳播機(jī)制的測(cè)試
? ? 1)REQUIRED
定義:如果有事務(wù)則加入事務(wù),如果沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的(默認(rèn)值)
操作1:將BlogServiceImpl和BlogServiceImpl2的事務(wù)傳播機(jī)制都修改為
@Transactional(propagation=Propagation.REQUIRED)
?結(jié)果1:
操作2:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.REQUIRED)
結(jié)果2:
總結(jié):
? ??? ??當(dāng)BlogServiceImpl提供事務(wù)的時(shí)候,BlogServiceImpl2的方法執(zhí)行使用當(dāng)前已有事務(wù),不再新建事務(wù);
如果出現(xiàn)異常則全部回滾;
? ? ? ? 當(dāng)BlogServiceImpl不創(chuàng)建事務(wù)的時(shí)候,BlogServiceImpl2的方法執(zhí)行發(fā)現(xiàn)沒(méi)有事務(wù)可用,自己新建事務(wù);
2)NOT_SUPPORTED
定義:Spring不為當(dāng)前方法開(kāi)啟事務(wù),相當(dāng)于沒(méi)有事務(wù)
? ? 操作:將BlogServiceImpl和BlogServiceImpl2的事務(wù)傳播機(jī)制都
修改為@Transactional(propagation=Propagation.NOT_SUPPORTED)
結(jié)果:
總結(jié):
? ? ? ? NOT_SUPPORTED相當(dāng)于沒(méi)有Spring事務(wù),每條執(zhí)行語(yǔ)句單獨(dú)執(zhí)行,單獨(dú)提交
3)REQUIRES_NEW
定義:不管是否存在事務(wù),都創(chuàng)建一個(gè)新的事務(wù),原來(lái)的方法掛起,新的方法執(zhí)行完畢后,繼續(xù)執(zhí)行老的事務(wù)
操作:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.REQUIRES_NEW)
結(jié)果:
? ? 總結(jié):
? ? ? ? REQUIRES_NEW為當(dāng)前方法創(chuàng)建一個(gè)新的事務(wù),并且當(dāng)前事務(wù)先提交,然后再提交老的事務(wù)
4)MANDATORY
定義:必須在一個(gè)已有的事務(wù)中執(zhí)行,否則報(bào)錯(cuò)
操作:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.MANDATORY),查看是否報(bào)錯(cuò)
? 結(jié)果:
? ? 總結(jié):? ??
? ? ? ? MANDATORY必須在已有事務(wù)下被調(diào)用,否則報(bào)錯(cuò)
? ? ? ? NOT_SUPPORTED執(zhí)行數(shù)據(jù)庫(kù)層面的事務(wù)操作,故當(dāng)前測(cè)試中,insert方法成功執(zhí)行,delete方法的拋錯(cuò)并不影響insert方法的執(zhí)行
5)NEVER
? ? 定義:必須在一個(gè)沒(méi)有的事務(wù)中執(zhí)行,否則報(bào)錯(cuò)
操作:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.MANDATORY),查看是否報(bào)錯(cuò)
? 結(jié)果:
? ? 總結(jié):
? ? ? ? NEVER必須在沒(méi)有事務(wù)的方法中執(zhí)行,否則報(bào)錯(cuò);
? ? ? ? save方法開(kāi)啟一個(gè)事務(wù),還沒(méi)來(lái)及提交發(fā)現(xiàn)delete方法報(bào)錯(cuò),只能回滾事務(wù)
? 6)SUPPORTS
定義:如果其他bean調(diào)用這個(gè)方法時(shí),其他bean聲明了事務(wù),則就用這個(gè)事務(wù),如果沒(méi)有聲明事務(wù),那就不用事務(wù)
操作1:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.SUPPORTS)
結(jié)果1:
操作1:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.SUPPORTS)
結(jié)果1:
? ??總結(jié):
? ? ? ? SUPPORTS類型的事務(wù)傳播機(jī)制,是否使用事務(wù)取決于調(diào)用方法是否有事務(wù),如果有則直接用,如果沒(méi)有則不使用事務(wù)
7)NESTED
定義:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行與REQUIRED類似的操作
操作1:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.NESTED)
?結(jié)果1:
操作2:將BlogServiceImpl事務(wù)傳播機(jī)制修改為@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍為@Transactional(propagation=Propagation.NESTED)
?結(jié)果2:
? ? 總結(jié):
? ? ? ? save方法創(chuàng)建一個(gè)事務(wù),則再調(diào)用delete方法時(shí),直接在該事務(wù)的基礎(chǔ)上創(chuàng)建一個(gè)嵌套事務(wù),本質(zhì)上還是同一個(gè)事務(wù),做一次提交;
? ? ? ? save方法不創(chuàng)建事務(wù),則調(diào)用delete方法時(shí),直接創(chuàng)建一個(gè)新的事務(wù),單獨(dú)提交
4.注意事項(xiàng)
1)REQUIRED
? ? ? ? 當(dāng)兩個(gè)方法的傳播機(jī)制都是REQUIRED時(shí),如果一旦發(fā)生回滾,兩個(gè)方法都會(huì)回滾
? 2)REQUIRES_NEW
? ? ? ? 當(dāng)delete方法傳播機(jī)制為REQUIRES_NEW,會(huì)開(kāi)啟一個(gè)新的事務(wù),并單獨(dú)提交方法,所以save方法的回滾并不影響delete方法事務(wù)提交
? 3)NESTED
? ? ? ? 當(dāng)save方法為REQUIRED,delete方法為NESTED時(shí),delete方法開(kāi)啟一個(gè)嵌套事務(wù);
? ? ? ? 當(dāng)save方法回滾時(shí),delete方法也會(huì)回滾;反之,如果delete方法回滾,則并不影響save方法的提交