Spring下面的@Transactional注解标志的讲解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Spring下⾯的@Transactional注解标志的讲解
最近在开发中对Spring中的事务标记@Transactional⽤的⽐较多,今天上⽹收集了⼀些内容,做⼀个简单的总结~~~
在service类前加上@Transactional,声明这个service所有⽅法需要事务管理。
每⼀个业务⽅法开始时都会打开⼀个事务。
Spring默认情况下会对运⾏期例外(RunTimeException)进⾏事务回滚。
这个例外是unchecked
如果遇到checked意外就不回滚。
如何改变默认规则:
1 让checked例外也回滚:在整个⽅法前加上 @Transactional(rollbackFor=Exception.class)
2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
3 不需要事务管理的(只查询的)⽅法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
如果在整个⽅法运⾏前就不会开启事务
还可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成⼀个只读事务,可以提⾼效率。
此外需要注意:如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。
各种属性的意义:
REQUIRED:业务⽅法需要在⼀个容器⾥运⾏。
如果⽅法运⾏时,已经处在⼀个事务中,那么加⼊到这个事务,否则⾃⼰新建⼀个新的事务。
NOT_SUPPORTED:声明⽅法不需要事务。
如果⽅法没有关联到⼀个事务,容器不会为他开启事务,如果⽅法在⼀个事务中被调⽤,该事务会被挂起,调⽤结束后,原先的事务会恢复执⾏。
REQUIRESNEW:不管是否存在事务,该⽅法总汇为⾃⼰发起⼀个新的事务。
如果⽅法已经运⾏在⼀个事务中,则原有事务挂起,新的事务被创建。
MANDATORY:该⽅法只能在⼀个已经存在的事务中执⾏,业务⽅法不能发起⾃⼰的事务。
如果在没有事务的环境下被调⽤,容器抛出例外。
SUPPORTS:该⽅法在某个事务范围内被调⽤,则⽅法成为该事务的⼀部分。
如果⽅法在该事务范围外被调⽤,该⽅法就在没有事务的环境下执⾏。
NEVER:该⽅法绝对不能在事务范围内执⾏。
如果在就抛例外。
只有该⽅法没有关联到任何事务,才正常执⾏。
NESTED:如果⼀个活动的事务存在,则运⾏在⼀个嵌套的事务中。
如果没有活动事务,则按REQUIRED属性执⾏。
它使⽤了⼀个单独的事务,这个事务拥有多个可以回滚的保存点。
内部事务的回滚不会对外部事务造成影响。
它只对
DataSourceTransactionManager事务管理器起效。
// 如果有事务,那么加⼊事务,没有的话新建⼀个(不写的情况下)
@Transactional(propagation=Propagation.REQUIRED)
// 容器不为这个⽅法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
// 不管是否存在事务,都创建⼀个新的事务,原来的挂起,新的执⾏完毕,继续执⾏⽼的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
// 必须在⼀个已有的事务中执⾏,否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)
// 必须在⼀个没有的事务中执⾏,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
// 如果其他bean调⽤这个⽅法,在其他bean中声明事务,那就⽤事务.如果其他bean没有声明事务,那就不⽤事务.
@Transactional(propagation=Propagation.SUPPORTS)
@Transactional(propagation=Propagation.NESTED)
// readOnly=true只读,不能更新,删除
@Transactional (propagation = Propagation.REQUIRED,readOnly=true)
// 设置超时时间
@Transactional (propagation = Propagation.REQUIRED,timeout=30)
// 设置数据库隔离级别
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)
@Transactional设置:
propagation:事务传播性设置,Propagation枚举类型。
Spring⽀持的事务传播属性包括7种:
PROPAGATION_MANDATORY:⽅法必须在事务中执⾏,否则抛出异常。
PROPAGATION_NESTED:使⽅法运⾏在嵌套事务中,否则和PROPAGATION_REQUIRED⼀样。
PROPAGATION_NEVER :当前⽅法永远不在事务中运⾏,否则抛出异常。
PROPAGATION_NOT_SUPPORTED:定义为当前事务不⽀持的⽅法,在该⽅法执⾏期间正在运⾏的事务会被暂停
PROPAGATION_REQUIRED:当前的⽅法必须运⾏在事务中,如果没有事务就新建⼀个事务。
新事务和⽅法⼀起开始,随着⽅法返回或者抛出异常时终⽌。
PROPAGATION_REQUIRED_NEW :当前⽅法必须新建⼀个事务,如果当前的事务正在运⾏则暂停。
PROPAGATION_SUPPORTS :规定当前⽅法⽀持当前事务,但是如果没有事务在运⾏就使⽤⾮事务⽅法执⾏。
isolation:事务隔离性级别设置,Isolation枚举类型
ISOLATION_DEFAULT :使⽤数据库默认的隔离级别
ISOLATION_COMMITTED:允许其他事务已经提交的更新(防⽌脏读取)
ISOLATION_READ_UNCOMMITTED:允许读取其他事务未提交的更新,会导致三个缺陷发⽣。
执⾏速度最快
ISOLATION_REPEATABLE_READ :除⾮事务⾃⾝更改了数据,否则事务多次读取的数据相同(防⽌脏数据,多次重复读取)
ISOLATION_SERIALIZABLE:隔离级别最⾼,可以防⽌三个缺陷,但是速度最慢,影响性能。
readOnly:读写性事务,只读性事务,布尔型
对数据库的操作中,查询是使⽤最频繁的操作,每次执⾏查询时都要从数据库中重新读取数据,有时多次读取的数据都是相同的,这样的数据操作不仅浪费了系统资源,还影响了系统速度。
对访问量⼤的程序来说,节省这部分资源可以⼤⼤提升系统速度。
将事务声明为只读的,那么数据库可以根据事务的特性优化事务的读取操作
timeout:超时时间,单位秒
事务可能因为某种原因很长时间没有反应,这期间可能锁定了数据库表,影响性能。
设置超时时间,如果超过该时间,事务⾃动回滚。
rollbackFor:⼀组异常类的实例,遇到时必须进⾏回滚
rollbackForClassname:⼀组异常类的名字,遇到时必须进⾏回滚
noRollbackFor:⼀组异常类的实例,遇到时必须不回滚
noRollbackForClassname:⼀组异常类的名字,遇到时必须不回滚
实例⼀:事务的回滚情况
1、默认情况对⾮运⾏时异常不进⾏回滚操作
/**
* 运⾏时异常默认事务回滚
* @throws Exception
*/
@Transactional
public void updateUser_1() throws Exception {
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","1");
userMap.put("userName","测试_01");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
throw new RuntimeException(); //抛出运⾏时异常默认情况下回滚事务---------------------->回滚 }
2、⾮运⾏时异常默认事务不回滚
/**
* ⾮运⾏时异常默认事务不回滚
* @throws Exception
*/
@Transactional
public void updateUser_3() throws Exception {
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","1");
userMap.put("userName","测试_01");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
throw new Exception(); //抛出⾮运⾏时异常默认情况下不回滚事务---------------------->不回滚 }
3、对所有异常事务都进⾏回滚操作
/**
* 对所有异常事务都进⾏回滚操作
* @throws Exception
*/
@Transactional(rollbackFor=Exception.class)
public void updateUser_6() throws Exception {
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","1");
userMap.put("userName","测试_01");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
throw new Exception(); //对所有异常事务都进⾏回滚操作事务---------------------->回滚
}
4、对所有异常事务都不进⾏回滚操作
/**
* 对所有异常事务都不进⾏回滚操作
* @throws Exception
*/
@Transactional(noRollbackFor=Exception.class)
public void updateUser_7() throws Exception {
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","1");
userMap.put("userName","测试_01");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
throw new RuntimeException(); //对所有异常,事务都不进⾏回滚操作事务---------------------->不回滚
}
实例⼆:对try{} catch{}的异常不回滚
/**
* 对于try{} catch{}的异常不进⾏回滚
* @throws Exception
*/
@Transactional
public void updateUser_5() throws Exception {
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","1");
userMap.put("userName","测试_01");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
try{
throw new RuntimeException(); //运⾏时异常被捕获,事务不回滚
}
catch(Exception e){
System.err.println("运⾏时异常被捕获,事务不回滚");
}
}
实例三:关于嵌套事务的回滚原则
说实话对于嵌套事务的测试,我现在仍然有⼀些疑问,⽐如说如果内部事务设置为
// 不管是否存在事务,都创建⼀个新的事务,原来的挂起,新的执⾏完毕,继续执⾏⽼的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
那这样的情况下,内部事务设置为Propagation.REQUIRES_NEW,外部事务设置为Propagation.REQUIRED,那么如果在内部事务执⾏完后之后,如果外部事务抛出了运⾏时异常,那么对于内部事务⽽⾔
由于是新开的⼀个单独的事务,这样的情况下内部事务应该不受外部事务的影响⽽回退,但是事实上经过测试,内部事务也是回滚的。
感兴趣的同学可以⼀起探讨啊~
在⽹上找了⼀些博客,下⾯贴出⼀篇⽐较好嵌套事务的博客:
1、外部事务抛异常,内部事务回滚(⼀般来说,只要外部开启事务,内部⽅法⽆论是否开启事务,只要出现相应的运⾏时异常,事务都将进⾏回滚操作)
/**
* 测试事务的相关信息
* @throws Exception
*/
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateUser() throws Exception {
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","1");
userMap.put("userName","测试_01");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
insideMethod_01(); //1 、异常不捕获事务---------------------->回滚
// insideMethod_02(); //2、异常捕获事务---------------------->不回滚
throw new RuntimeException(); //3、测试外部⽅法抛异常事务---------------------->回滚
// throw new Exception();//4、默认情况,⾮运⾏时异常事务---------------------->不回滚
}
/**
* 事务测试的内部⽅法内部⽅法抛运⾏时异常不捕获 </br>
*
* 事务会对内外⽅法进⾏回滚操作
*/
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void insideMethod_01(){
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","2");
userMap.put("id","2");
userMap.put("userName","测试_02");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
// int value=1/0;//运⾏时异常
}
2、内部事务抛异常,外部事务回滚(⼀般来说,只要外部开启事务,内部⽅法⽆论是否开启事务如何配置,只要出现相应的运⾏时异常,内外事务都将进⾏回滚操作)
/**
* 测试事务的相关信息
* @throws Exception
*/
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateUser() throws Exception {
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","1");
userMap.put("userName","测试_01");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
insideMethod_01(); //1 、异常不捕获事务---------------------->回滚
// insideMethod_02(); //2、异常捕获事务---------------------->不回滚
// throw new RuntimeException(); //3、测试外部⽅法抛异常事务---------------------->回滚
// throw new Exception();//4、默认情况,⾮运⾏时异常事务---------------------->不回滚
}
/**
* 事务测试的内部⽅法内部⽅法抛运⾏时异常不捕获 </br>
*
* 事务会对内外⽅法进⾏回滚操作
*/
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void insideMethod_01(){
Map<String,String> userMap=new HashMap<String,String>();
userMap.put("id","2");
userMap.put("id","2");
userMap.put("userName","测试_02");
userMap.put("password","654321");
userDao.updateUserByID(userMap);
int value=1/0;//运⾏时异常
}
注:这⾥还有⼀点在实际开发中需要注意,如果外部⽅法调⽤了远程的接⼝,由于它们不在⼀个事务控制中,类似于分布式事务,这种情况远程事务是⽆法进⾏回滚操作的~。