Spring 声明式事务管理介绍
说明:本文基于 jdk 1.8 ,spring-framework 5.1.7.RELEASE。
Spring 的声明式事务也就是通过配置的方式来管理事务,而不通过硬编码的方式来管理事务。关系型数据库对事务的主要操作包含:事务提交 , 事务回滚。Spring 事务管理的主要操作包含:事务提交,事务回滚,事务挂起 。 Spring 申明式事务管理是建立在 Spring AOP 技术的基础之上, 本质上也就是建立在动态代理对象技术之上 (JdkDynamicAopProxy , CglibAopProxy)。
Spring 事务传播机制介绍
1. 什么是事务的传播机制?
当事务函数 A 内部调用了另一个事务函数 B ,在调用到函数 B 后作用于函数 A 上的事务是否作用于函数 B 内或者如何作用,这就是事务的传播。
2. Spring 支持的事务传播机制
2.1 PROPAGATION_REQUIRED
该函数必须有一个事务,如果不存在就创建一个,如果已经有一个存在的事务那么就使用这个存在的事务。在这种传播机制下使用的始终是同一个事务。
2.2 PROPAGATION_SUPPORTS
支持当前存在的事务; 如果不存在则执行非事务性。
2.3 PROPAGATION_MANDATORY
强制性的支持当前事务,如果当前事务不存在则抛出异常。
2.4 PROPAGATION_REQUIRES_NEW
创建一个新事务,挂起当前已经存在的事务。
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());
}
}