說明:本文基於 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()); } }