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());
    }
}
相關文章
相關標籤/搜索