源无极

导航

 

一、先简单介绍一下Spring事务的传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,

此时有若干选项可以指定一个事务性方法的执行行为。

TransactionDefinition定义中包括了如下几个表示传播行为的常量:

TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,

则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,

如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,

则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,

如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,

如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,

则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,

则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,

则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

二、Spring事务的回滚机制

Spring的AOP即声明式事务管理默认是针对unchecked exception回滚。

Spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行

commit or rollback(Spring默认取决于是否抛出runtimeException)。

       如果你在方法中有try{}catch(Exception e){}处理,那么try里面的代码块就脱离了事务的管理

若要事务生效需要在catch中throw new RuntimeException ("xxxxxx");

 

补充:简单介绍一下@Transactional注解底层实现方式吧,毫无疑问,是通过动态代理,

那么动态代理又分为JDK自身和CGLIB,这个也不多赘述了,

(一)

第一点要注意的就是在@Transactional注解的方法中,再调用本类中的其他方法method2时,

那么method2方法上的@Transactional注解是不!会!生!效!的!

但是加上也并不会报错,拿图片简单帮助理解一下吧

 

 通过代理对象在目标对象前后进行方法增强,也就是事务的开启提交和回滚

那么继续调用本类中其他方法是怎样呢,如下图

 

        可见目标对象内部的自我调用,也就是通过this.指向的目标对象将不会执行方法的增强。

先说第二点需要注意的地方,等下说如何解决上面第一点的问题。


(二)

第二点就是@Transactional注解的方法必须是公共方法,就是必须是public修饰符!!!

至于这个的原因,发表下个人的理解吧,因为JVM的动态代理是基于接口实现的,

通过代理类将目标方法进行增强,想一下也是啦,没有权限访问那么你让我怎么进行,,,好吧,

这个我也没有深入研究底层,个人理解个人理解。

       在这里我也放个问题吧,希望有高手可以回复指点指点我,因为JVM动态代理是基于接口实现的,

那么是不是service层都要按照接口和实现类的开发模式,注解才会生效呢,

就是说controller层直接调用没有接口的service层,加了注解也一样不起作用吧,这个懒了,

没有测试,其一是因为没有人会这么开发吧,其二是我就认为是不起作用的,哈哈,,,

       下面来解决一下第一点的问题,如何在方法中调用本类中其他方法呢

通过AopContext.currentProxy ()获取到本类的代理对象,再去调用就好啦。

因为这个是CGLIB实现,所以要开启AOP,当然也很简单,

在springboot启动类上加上注解@EnableAspectJAutoProxy(exposeProxy = true)就可以啦,

这个依赖大家自行搜一下就好啦。要注意,注意,代理对象调用的方法也要是public修饰符,

否则方法中获取不到注入的bean,会报空指针错误。

 (一)一个类中调用另一个方法事务一致性

package com.po.service;
import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {
 @Autowired
    UserMapper userMapper;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    @Override
    @Transactional
    public void updateUser( ) {
        this.updateUser1();
        this.updateUser2();
    }
    public void updateUser1(){
        userPo1.setId(24);
        userPo1.setUsername("lanpo1");
        userMapper.updateUser(userPo1);
        //
        userPo2.setId(25);
        userPo2.setUsername("lanpo2");
        userMapper.updateUser(userPo2);
    }
    public void updateUser2(){
        userPo3.setId(27);
        userPo3.setUsername("lanpo3");
        userMapper.updateUser(userPo3);
        int i=1/0;//异常
        userPo4.setId(28);
        userPo4.setUsername("lanpo4");
        userMapper.updateUser(userPo4);

    }
}

 

dao

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    UserPo getUser(int id);
    void updateUser(UserPo userPo);
}

 

mapper.xml

<update id="updateUser" parameterType="com.po.entity.UserPo">
    UPDATE USER  SET username=#{username} WHERE id =#{id}
</update>

 

entity

 private Integer id;
    private String username;
    private String birthday;
    private String sex;

 

updateUser方法在一个事务中,更新失败

 

 

(二)Transactioanl失效的情况

package com.po.service;


import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {
 @Autowired
    UserMapper userMapper;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    @Override
    @Transactional
    public void updateUser( ) {
        this.updateUser1();
        this.updateUser2();
    }
    @Transactional
    public void updateUser1(){
        userPo1.setId(24);
        userPo1.setUsername("lanpo1");
        userMapper.updateUser(userPo1);
        //
        userPo2.setId(25);
        userPo2.setUsername("lanpo2");
        userMapper.updateUser(userPo2);
    }
    @Transactional
    public void updateUser2(){
        userPo3.setId(27);
        userPo3.setUsername("lanpo3");
        userMapper.updateUser(userPo3);
        int i=1/0;//异常
        userPo4.setId(28);
        userPo4.setUsername("lanpo4");
        userMapper.updateUser(userPo4);

    }
}

 

 

 更新失败,可见在同类中被调用的方法加@Transactional无效

(三)

package com.po.service;
import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {
 @Autowired
    UserMapper userMapper;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    @Override
    @Transactional
    public void updateUser( ) {
        UserServiceImpl currentProxy = (UserServiceImpl) AopContext.currentProxy();
        currentProxy.updateUser1();
        currentProxy.updateUser2();
    }
   
    public void updateUser1(){
        userPo1.setId(24);
        userPo1.setUsername("lanpo1");
        userMapper.updateUser(userPo1);
        //
        userPo2.setId(25);
        userPo2.setUsername("lanpo2");
        userMapper.updateUser(userPo2);
    }

    public void updateUser2(){
        userPo3.setId(27);
        userPo3.setUsername("lanpo3");
        userMapper.updateUser(userPo3);
        int i=1/0;//异常
        userPo4.setId(28);
        userPo4.setUsername("lanpo4");
        userMapper.updateUser(userPo4);

    }
}

 

 

 

 更新失败。

      上面第一和第三情况不管使不使用代理调用方法1和方法2,方法 updateUser()

都处在一个事务中,

四条更新操作全部失败

那么有人可能会有疑问了,在方法1和方法2上都加@Transactional注解呢?

答案是结果和上面是一致的。

    小结:只要方法updateUser上有注解,并且方法1和方法2都处于当前事务中

(不使用代理调用,方法1和方法2上的@Transactional注解是不生效的;使用代理,

需要方法1和方法2都处在updateUser方法的事务中,默认或者嵌套事务均可,

当然也可以不加@Transactional注解),那么整体保持事务一致性。

 

四、要方法1和方法2均单独保持事务一致性怎么办呢(保留疑问)

    如果不是用代理调用@Transactional注解是不生效的,

所以一定要使用代理调用实现,然后让方法1和方法2分别单独开启新的事务,

便OK啦。下面摆上图片。

(一)

    @Override
    public void updateUser( ) {
        UserServiceImpl currentProxy = (UserServiceImpl) AopContext.currentProxy();
        currentProxy.updateUser1();
        currentProxy.updateUser2();
    }
    @Transactional
    public void updateUser1(){
        userPo1.setId(24);
        userPo1.setUsername("lanpo1");
        userMapper.updateUser(userPo1);
        //
        userPo2.setId(25);
        userPo2.setUsername("lanpo2");
        userMapper.updateUser(userPo2);
    }
    @Transactional
    public void updateUser2(){
        userPo3.setId(27);
        userPo3.setUsername("lanpo3");
        userMapper.updateUser(userPo3);
        int i=1/0;//异常
        userPo4.setId(28);
        userPo4.setUsername("lanpo4");
        userMapper.updateUser(userPo4);

    }

 

五、通过将方法1放到另一个类达到单独保持事务一致问题

(一)查看将方法1放到另一个类达到事务统一

package com.po.service;
import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import org.springframework.aop.framework.AopContext;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl implements UserService {
 @Autowired
    UserMapper userMapper;
 @Autowired
 UserServiceImpl2 userServiceImpl2;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    @Override
    @Transactional
    public void updateUser( ) {
   
        userServiceImpl2.updateUser1();
        this.updateUser2();
    }
  

    @Override
    public void updateUser2(){
        userPo3.setId(27);
        userPo3.setUsername("lanpo3");
        userMapper.updateUser(userPo3);
        int i=1/0;//异常
        userPo4.setId(28);
        userPo4.setUsername("lanpo4");
        userMapper.updateUser(userPo4);

    }
}

 

UserServiceImpl2   
package com.po.service;
import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import org.springframework.aop.framework.AopContext;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl2   {
 @Autowired
    UserMapper userMapper;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    public void updateUser1(){
        userPo1.setId(24);
        userPo1.setUsername("lanpo1");
        userMapper.updateUser(userPo1);
        //
        userPo2.setId(25);
        userPo2.setUsername("lanpo2");
        userMapper.updateUser(userPo2);
    }
   
}

 

 

 方法一方法二事务一致。

 

五、

@Transactiona 放在类上相当于该类的每一个方法都加上
@Transactiona ,而且方法上的会覆盖类上的。
package com.po.service;
import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import org.springframework.aop.framework.AopContext;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class UserServiceImpl implements UserService {
 @Autowired
    UserMapper userMapper;
 @Autowired
 UserServiceImpl2 userServiceImpl2;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    @Override

    public void updateUser( ) {
        this.updateUser1();
        this.updateUser2();
    }

    @Override
    public void updateUser1(){
        userPo1.setId(24);
        userPo1.setUsername("lanpo1");
        userMapper.updateUser(userPo1);
        //
        userPo2.setId(25);
        userPo2.setUsername("lanpo2");
        userMapper.updateUser(userPo2);
    }

    @Override
    public void updateUser2(){
        userPo3.setId(27);
        userPo3.setUsername("lanpo3");
        userMapper.updateUser(userPo3);
        int i=1/0;//异常
        userPo4.setId(28);
        userPo4.setUsername("lanpo4");
        userMapper.updateUser(userPo4);

    }
}

 

 

 

六、 @Transactional和try catch的一起使用注意

package com.po.service;
import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import org.springframework.aop.framework.AopContext;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service

public class UserServiceImpl implements UserService {
 @Autowired
    UserMapper userMapper;
 @Autowired
 UserServiceImpl2 userServiceImpl2;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    @Override
    @Transactional
    public void updateUser( ) {
   
       this.updateUser2();
    }

   
    @Override
   
    public void updateUser2(){
        try {
            userPo3.setId(27);
            userPo3.setUsername("lanpo3");
            userMapper.updateUser(userPo3);
            int i=1/0;//异常
        }catch (Exception e){
            userPo4.setId(28);
            userPo4.setUsername("lanpo4");
            userMapper.updateUser(userPo4);

        }

    }
}

 

 

 发现:虽然有异常但是没有回滚

(二)

package com.po.service;
import com.po.dao.UserMapper;
import com.po.entity.UserPo;
import org.springframework.aop.framework.AopContext;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service

public class UserServiceImpl implements UserService {
 @Autowired
    UserMapper userMapper;
 @Autowired
 UserServiceImpl2 userServiceImpl2;
    UserPo userPo1 = new UserPo();
    UserPo userPo2 = new UserPo();
    UserPo userPo3 = new UserPo();
    UserPo userPo4 = new UserPo();

    @Override
    @Transactional
    public void updateUser( ) {
  
       this.updateUser2();
    }


    @Override
 
    public void updateUser2(){
        try {
            userPo3.setId(27);
            userPo3.setUsername("lanpo3");
            userMapper.updateUser(userPo3);
            int i=1/0;//异常
        }catch (Exception e){
            userPo4.setId(28);
            userPo4.setUsername("lanpo4");
            userMapper.updateUser(userPo4);
            /*
            注意:1.try代码会脱离Spring的事务,必须要在catch中抛出
            运行时异常才能保证事务一致性
               2. 56-58行代码也是处在同一事务中的
            */
           throw new RuntimeException("事务异常");
        }

    }
}

 

 

 

 

 完毕。

 

posted on 2020-03-12 14:39  源无极  阅读(324)  评论(0)    收藏  举报