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注解进行事务的使用。

 

问题

  1. 事务不回滚

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转为代理模式进行事务  

  

 

posted @ 2018-05-16 17:41  _陈昱先  阅读(899)  评论(0)    收藏  举报