说明:本文基于 jdk 1.8 ,spring-framework 5.1.7.RELEASE。java
Spring 的声明式事务也就是经过配置的方式来管理事务,而不经过硬编码的方式来管理事务。关系型数据库对事务的主要操做包含:事务提交 , 事务回滚。Spring 事务管理的主要操做包含:事务提交,事务回滚,事务挂起 。 Spring 申明式事务管理是创建在 Spring AOP 技术的基础之上, 本质上也就是创建在动态代理对象技术之上 (JdkDynamicAopProxy , CglibAopProxy)。面试
当事务函数 A 内部调用了另外一个事务函数 B ,在调用到函数 B 后做用于函数 A 上的事务是否做用于函数 B 内或者如何做用,这就是事务的传播。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 事务管理是和当前线程绑定的,须要跨函数传递的变量都存储在 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 事务相关问题时,会常常问这么一个问题 。
问题 :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()); } }