spring boot -- 事务
事务
基础
分为 编程式事务 与 声明式事务
import org.springframework.transaction.annotation
@Transactional() @Transactional
在service层使用@Transcational
@Transactional使用在类上表示类中的方法都是事务
一般在使用单元测试时,都会添加@Rollback来进行数据的回滚
@EnableTransactionManagement (@SpringBootApplication)
JPA默认开启transactional,且查询都为readOnly=true,可以看源码SimpleJpaRepository
spring boot专门配置事务的类为: org.springframework.boot.autoconfigure.transaction.TransactionalAutoConfiguration,
但是因为在TransactionManagerAutoConfiguration中开启了对声明式事务的支持
@ConditionalOnMissingBean
@Configuration
@EnableTransactionManagement
所以在Spring Boot中,不需要显示声明
在Spring Boot中,当我们使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖的时候,框 架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager。所以我们不需要任何额外 配置就可以用@Transactional注解进行事务的使用。
问题
- 事务不回滚
a) 启动代理模式
b) shiro导致SpringBoot事务不起效
原因:shiro在启动配置时Spring还没有启动
解决方法:把原来在ShiroConfig里面初始化的getUserRealm与securityManager方法移动到一个新建的Spring监听器中进行初始化
c) 默认情况下,只对unchecked异常进行事务回滚,如果是checked异常,则不回滚
unchecked:派生于Error,RuntimeException(如空指针,1/0) 空指针,文件读写
checked: 其他的继承自java.lang.Exception的异常,如IOException,TimeOutException
解决方法: 对于checked异常不回滚的 增加注解
@Transaction(rollbackFor=Exception.class)
如果异常被try{} catch{}到,事务不会回滚
使用回滚需要抛出异常 try{} catch{throw new RuntimeException}
d) 数据库需要支持回滚
Innodb 与 myisam的区别
e) 是否开启对注解的解析
@EnableTransactionManagement
f) spring是否扫描到该包
g) 是否是方法的调用
声明式事务是通通过AOP动态代理实现的,这样会产生一个代理类来做事务管理,而目标类(service)本身是不能感知代理类的存在的。
对于加了@Transactional注解的方法来说,在调用代理类的方法时,会先通过拦截器TransactionInterceptor开启事务,然后在调用目标类的方法,最后在调用结束后,TransactionInterceptor 会提交或回滚事务,大致流程如下

解决方案
使用代理模式,controller调用service的代理,进行操作
以下代码是将所有的@Service注释的类,在加载其特定方法时,改为事务模式
package demo.d.proxy; import java.util.Properties; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.interceptor.TransactionInterceptor; public class TransactionalConfig { @Autowired PlatformTransactionManager transactionManager; private static String cut = "execution (* *.service.*.*(..))||execution (* *.service.impl.*.* (..))"; //将特定函数进行事务化 public DefaultPointcutAdvisor getAdvisor(){ //定义切点 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(cut); DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(); advisor.setPointcut(pointcut); Properties attributes = new Properties(); attributes.setProperty("save*", "PROPATAGION,-Exception"); //...... -Exception 代表回滚 TransactionInterceptor advice = new TransactionInterceptor(transactionManager,attributes); advisor.setAdvice(advice); return advisor; } }
package demo.d.proxy; import org.springframework.aop.framework.ProxyFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class DynamicProxy { @Autowired TransactionalConfig transactionalConfig; //代理模式 public <T> T getProxy(Object object){ ProxyFactoryBean factory = new ProxyFactoryBean(); factory.setTarget(object); factory.addAdvisor(transactionalConfig.getAdvisor()); return (T)factory.getObject(); } }
BeanPostProcessor 在bean加载前后进行
package demo.d.proxy; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; @Component public class Initialize implements BeanPostProcessor{ @Autowired DynamicProxy dynamicProxy; @Override //对每个@Service注解的bean,使其在生成时,改为代理模式 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean.getClass().isAnnotationPresent(Service.class)){ bean = dynamicProxy.getProxy(bean); } return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } }
spring 的声明式事务在注解时自动开启了代理模式 所以在编程式事务的AOP注解时,如果没有给指定的Service启用代理,则会无效 因此需要把service转为代理模式进行事务

浙公网安备 33010602011771号