Spring 声明式事务管理(1)

Spring 声明式事务管理介绍

    说明:本文基于 jdk 1.8 ,spring-framework 5.1.7.RELEASE。java

    Spring 的声明式事务也就是经过配置的方式来管理事务,而不经过硬编码的方式来管理事务。关系型数据库对事务的主要操做包含:事务提交 , 事务回滚。Spring 事务管理的主要操做包含:事务提交,事务回滚,事务挂起 。 Spring 申明式事务管理是创建在 Spring AOP 技术的基础之上, 本质上也就是创建在动态代理对象技术之上 (JdkDynamicAopProxy , CglibAopProxy)。面试

  

Spring 事务传播机制介绍

1. 什么是事务的传播机制?

        当事务函数 A 内部调用了另外一个事务函数 B ,在调用到函数 B 后做用于函数 A 上的事务是否做用于函数 B 内或者如何做用,这就是事务的传播。spring

2. Spring 支持的事务传播机制

        2.1    PROPAGATION_REQUIRED 数据库

              该函数必须有一个事务,若是不存在就建立一个,若是已经有一个存在的事务那么就使用这个存在的事务。在这种传播机制下使用的始终是同一个事务。ide

              

        2.2    PROPAGATION_SUPPORTS 函数

             支持当前存在的事务; 若是不存在则执行非事务性。ui

        2.3    PROPAGATION_MANDATORYthis

                强制性的支持当前事务,若是当前事务不存在则抛出异常。编码

        2.4    PROPAGATION_REQUIRES_NEWspa

                建立一个新事务,挂起当前已经存在的事务。

                

        2.5    PROPAGATION_NOT_SUPPORTED

                不支持当前事务; 而是老是以非事务方式执行。

        2.6    PROPAGATION_NEVER

                从不支持事务,若是存在当前事务抛出异常。

        2.7    PROPAGATION_NESTED

                使用具备多个保存点的单个物理事务,它能够回滚到该事务。 这种部分回滚容许内部事务做用域触发其做用域的回滚,外部事务可以继续物理事务,尽管已经回滚了一些操做。 此设置一般映射到JDBC保存点,所以它仅适用于JDBC资源事务。 

 

Spring 事务管理的线程依赖性

        Spring 事务管理是和当前线程绑定的,须要跨函数传递的变量都存储在 ThreadLocal 中好比数据库链接 Connection 对象。因此 Spring 的事务传播也只能是基于当前线程,若是在另外一个线程中调用一个事务函数那么就是一个新的事务了。TransactionSynchronizationManager  是用来保存一些事务操做过程当中的共享数据。

public abstract class TransactionSynchronizationManager {

	private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");

	private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");

	private static final ThreadLocal<String> currentTransactionName =
			new NamedThreadLocal<>("Current transaction name");

	private static final ThreadLocal<Boolean> currentTransactionReadOnly =
			new NamedThreadLocal<>("Current transaction read-only status");

	private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
			new NamedThreadLocal<>("Current transaction isolation level");

	private static final ThreadLocal<Boolean> actualTransactionActive =
			new NamedThreadLocal<>("Actual transaction active");

    // ......

}

 

Spring 事务代理对象说明

        去面试的时候面试官问到 spring 事务相关问题时,会常常问这么一个问题 。  

        问题 :saveUser(UserEntity user) 函数内部调用了 getUserById(Long uid) 函数,那么 getUserById(Long uid) 可否被事务做用? 

        答案: 不能。由于在这个 UserServiceImpl 内部调用本身对象内部的函数使用的是本对象(也就是 this) , 而这个 this 并非一个代理对象因此并不会有事务做用。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public UserEntity getUserById(Long uid) {
        return null;
    }
    
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void saveUser(UserEntity user) {
        // save user
        
        this.getUserById(user.getId());
    }
}

    若是这个问题你回答对了,面试官可能会问若是我想有事务做用的调用 getUserById(Long uid) 函数该如何作?这里有两种办法,第一种是将 UserServiceImpl 的代理对象注入到 UserServiceImpl 中 。@Autowired UserService proxyUserService 。 这个 proxyUserService  就是被事务代理的对象,用这个对象去调用就可让事务起做用。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private UserService proxyUserService;



    @Transactional(rollbackFor = Throwable.class)
    @Override
    public UserEntity getUserById(Long uid) {
        return null;
    }
    
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void saveUser(UserEntity user) {
        // save user
        
        proxyUserService.getUserById(user.getId());
    }
}

       

        第二种方法就是使用 AopContext 获取到当前类的代理对象,而后用获取到的代理对象去进行调用。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public UserEntity getUserById(Long uid) {
        return null;
    }
    
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void saveUser(UserEntity user) {
        // save user
        
        UserService userService = (UserService) AopContext.currentProxy();
        userService .getUserById(user.getId());
    }
}
相关文章
相关标签/搜索