spring事务失效的原因,spring事务使用场景
一、访问权限Java的访问权限主要有:私有、默认、受保护和公共,它们的权限依次变大。
如果我们在开发时定义了错误的访问权限,就会在事务中造成问题。
@服务
公共类演示服务{
@事务性
私有void查询(演示){
}
}
我们可以看一下源代码,可以理解spring transaction的实现在AbstractAllbackTransactionAttribute源类的computeTransactionAttribute方法中有一个判断。如果目标方法不是公共的,则TransactionAttribute返回null,即不支持事务。
第二,方法是用final修饰的。我们在学习的时候都知道,当一个方法不想被子类继承的时候,就会加上关键字final。这种书写方法通常是好的,但是如果该方法用于事务,它就不起作用。
@服务
公共类演示服务{
@事务性
公共最终无效查询(演示)
}
}这会导致事务失效,因为spring transaction的底层实现使用了proxy,aop,proxy类是通过jdk的动态代理或者cglib生成的,事务功能是在proxy类中实现的。如果方法被final修改,无法重写,则不能添加事务函数。
正如idea在这里所建议的,类似地,如果方法是静态的,它就不会工作。
因为静态方法属于类,不是对象,不能被重写,所以不可能实现事务。
第三,方法的内部调用调用同一个类的服务中的其他事务方法。
@服务
公共类演示服务{
@事务性
公共void查询(演示){
保存(演示);
}
@事务性
公共作废保存(演示演示){
}
}可以看到,query方法调用save方法。由于spring的事务实现是由于aop生成代理,直接调用这个对象,所以不会生成任何事务。
解决方法:
1.添加一个服务,将一个事务的方法移到新添加的服务方法中,然后注入并再次调用它。
@服务
公共类DemoTwoService {
@事务性
公共作废保存(演示演示){
}
}
@服务
公共类演示服务{
@自动连线
DemoTwoService demoTwoService
@事务性
公共void查询(演示){
demoTwoService.save(演示);
}
}2.把自己注入自己的班级。
@服务
公共类演示服务{
@自动连线
DemoService demoService
@事务性
公共void查询(演示){
demoService.save(演示);
}
@事务性
公共作废保存(演示演示){
}
}因为这种基于spring的L3缓存的编写方式不会导致循环依赖的问题。
3.传递AopContentent
@服务
公共类演示服务{
@事务性
公共void查询(演示){
demo service demo service=(demo service)AOP context . current proxy();
demoService.save(演示);
}
@事务性
公共作废保存(演示演示){
}
}四。不受spring管理。使用spring事务时,对象需要由spring管理,也就是需要创建beans。一般我们会添加@Controller、@Service、@Component、@Repository等注释。可以自动实现bean实例化和依赖注入的功能。如果忘记添加,也会导致交易无效。
动词(verb的缩写)多线程调用@Service
公共类演示服务{
@自动连线
DemoTwoService demoTwoService
@事务性
公共void查询(演示){
新线程(()- {
demoTwoService.save(演示);
}).start();
}
}从上面的例子我们可以知道,query调用了事务方法save,但是事务方法是在另一个线程中调用的。这会导致两个方法在不同的线程中,获得不同的数据库连接,所以会是两个不同的事务。如果save方法抛出异常,query就不可能回滚。看了spring的源代码,我们可以知道,spring的事务是通过连接数据库来实现的。当前线程保存一个映射、键数据源和值数据库连接。事务实际上指向同一个连接。只有使用相同的数据库连接,才能同时提交和回滚。如果不同线程中的数据库连接不相同,则事务也不相同。
私有静态最终线程本地映射对象,对象资源=
新命名的ThreadLocal(“事务性资源”);不及物动词设计的表不支持事务。众所周知,MyISAM是mysql5之前的默认数据库引擎。也许一些老项目还在使用,但它不支持事务。
七。交易未开始。如果没有创建springboot项目,可能会导致这样的问题,因为springboot项目有自动组装的类DatasourceTransactionManagerOutConfiguration,并且默认情况下已经启动了事务。只需配置spring.datasource参数。如果是spring项目,需要在applicationContext.xml中配置,否则事务不会生效。
!-配置事务管理器-
bean id=transactionManager
属性名= data source ref= data source /属性
/bean
tx:建议id=建议事务管理器=事务管理器
tx:属性
tx:方法名=* 传播=必需/
/tx:属性
/tx:建议
!-用切入点切入生意-
aop:配置
aop:切入点表达式=execution(* com.demo.*。*(.)) id=pointcut/
AOP:advisor advice-ref= advice pointcut-ref= pointcut /
/aop:config,而切入点的不匹配也会造成事务失效。
八。错误事务传播我们可以在使用@Transactional注释时指定传播参数。
该参数的功能是指定事务的传播特征,
Spring目前支持七个传播特性:
如果当前上下文中有事务,则需要加入该事务;如果没有事务,则创建一个事务,这是传播属性的默认值。
支持如果当前上下文中有事务,则支持该事务加入该事务;如果没有事务,则以非事务方式执行。
如果当前上下文中有事务,则Mandatory将引发异常。
Requires _ new每次创建一个新的事务,同时在上下文中挂起该事务。在当前新创建的事务完成之后,上下文事务被恢复,然后被执行。
Not _ supported如果当前上下文中有事务,则当前事务被挂起,然后在没有事务的环境中执行新方法。
如果当前上下文中有事务,则从不引发异常,否则在无事务的环境中执行代码。
嵌套如果当前上下文中有事务,则执行嵌套事务;如果没有事务,则创建一个新事务。
如果我们在手动设置传播参数时设置了错误的传播属性
@服务
公共类演示服务{
@Transactional(传播=传播。从不)
公共void查询(演示){
}
}我们可以看到query的事务传播设置为propagation。这种类型的传播不支持事务,并且会抛出异常,
目前,有三种传播特性支持事务:REQUIRED、REQUIRES_NEW和NESTED。
九。我们捕获了异常事务,并且没有回滚它。也许我们手动尝试了…在我们写代码的时候在代码中捕捉它。
@事务性
公共void查询(演示){
尝试{
保存(演示);
} catch(异常e) {
system . out . println(“exception”);
}
}在这种情况下,spring事务将不会回滚,因为我们手动捕获了异常,然后没有手动抛出它。如果我们想要spring事务的正常回滚,我们必须抛出它能处理的异常。如果没有抛出异常,spring会认为程序没有问题。
X.手动抛出其他异常。我们没有手动捕获异常,但是如果抛出的异常不正确,spring事务将不会回滚。
@事务性
公共void查询(演示)引发异常{
尝试{
保存(演示);
} catch(异常e) {
抛出新的异常(e);
}
}以上,我们捕捉到了异常,然后手动抛出异常,事务不会再回滚,因为spring事务默认情况下不会回滚异常(非RuntimeException),只回滚运行时异常(Runtime Exception)和错误(Error)。
XI。自定义回滚异常在使用@Transactional注释声明事务时,有时我们希望自定义回滚异常,spring也支持。您可以通过设置rollbackFor参数来完成此功能。
但是如果这个参数的值设置错了,就会导致一些问题。
@ Transactional(roll back for=business exception . class)
公共void查询(演示)引发异常{
保存(演示);
}以上是我们的自定义业务例外。如果程序在执行上述代码并保存和更新数据时报告错误,就会抛出SqlException和DuplicateKeyException等异常。而BusinessException是我们自定义的异常,上报的异常不属于BusinessException,所以不会回滚事务。即使rollbackFor有默认值,也需要开发者在阿里巴巴的开发者规范中重新指定这个参数。
因为如果使用默认值,一旦程序抛出异常,事务就无法回滚,会造成很大的bug。因此,一般情况下建议将该参数设置为:Exception或Throwable。
十二。嵌套事务回滚太多@Service
公共类演示服务{
@自动连线
private DemoTwoService DemoTwoService;
@自动连线
私人DemoDao demoDao
公众的
@ Transactional(roll back for=exception . class)
公共void查询(演示)引发异常{
demoDao.save(演示);
demoTwoService.save(演示);
}
}本来只是想回滚demoServise.save(),而不是demo Dao . save(demo);但在这种情况下两者都将回滚,因为demotowservice . save(demo);没有捕获到异常,它被抛出,导致query回滚,所以两个同时回滚。
解决方法:
@服务
公共类演示服务{
@自动连线
private DemoTwoService DemoTwoService;
@自动连线
私人DemoDao demoDao
公众的
@ Transactional(roll back for=exception . class)
公共void查询(演示)引发异常{
demoDao.save(演示);
尝试{
demoTwoService.save(演示);
}最后{
}
}
}你可以把内部嵌套的事务放在try/catch里,不要继续抛出异常。这确保了如果内部嵌套事务中发生异常,只有内部事务将被回滚,而不会影响外部事务。
十三。编程式事务的事务失效基于@Transactional注释。我们称这种事务为:声明式事务。
事实上,spring还提供了另一种创建事务的方式,即通过手工编写代码实现的事务,我们称之为程序化事务。
@服务
公共类演示服务{
@自动连线
私有transaction template transaction template;
@ Transactional(roll back for=exception . class)
公共void查询(演示)引发异常{
transactionTemplate.execute((状态- {
保存(演示);
返回布尔值。真实;
}));
}
@事务性
公共作废保存(演示演示){
}
}在spring中,为了支持程序化事务,专门提供了一个类TransactionTemplate。在其执行方法中,实现了事务功能。
使用TransactionTemplate的编程事务可以
避免spring aop导致的事务失效问题。
可以用更小的粒度来控制事务的范围。
温馨提示:本文中的查询加事务不符合开发的要求,但是有点懒就不改了。
版权归作者所有:原创作品来自博主、程序员,转载授权请联系作者,否则将追究法律责任。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。