@Transactional註解管理事務和手動提交事務

spiderMan1-1發表於2024-10-29

1、@Transacational註解

(1)註解裡面的屬性:

  • propagation: 用於設定事務傳播屬性,該屬性型別為Propagation列舉,預設是Propagation.REQUIRED
  • issolation: 用於設定事務的隔離級別,該屬性型別為Isolation列舉,預設是Isolation.DEFAULT
  • readOnly: 用於設定該方法對資料庫的操作是否是隻讀,該屬性為boolean,預設為false
  • timeout: 用於設定本操作與資料庫連線的超時時限,單位為秒,型別int,預設為-1,即沒有時限
  • rollbackFor 指定需要回滾的異常類。型別為 Class[],預設值為空陣列。當然,若只有一個異常類時,可以不使用陣列。
  • rollbackForClassName:指定需要回滾的異常類類名。型別為 String[],預設值為空陣列。當然,若只有一個異常類時,可以不使用陣列。
  • noRollbackFor:指定不需要回滾的異常類。型別為 Class[],預設值為空陣列。當然,若只有一個異常類時,可以不使用陣列。
  • noRollbackForClassName: 指定不需要回滾的異常類類名。型別為 - String[],預設值為空陣列。當然,若只有一個異常類時,可以不使用陣列。

需要注意的是,@Transactional 若用在方法上,只能用於 public 方法上。對於其他非 public 方法,如果加上了註解 @Transactional,雖然 Spring 不會報錯,但不會將指定事務織入到該方法中。因為 Spring 會忽略掉所有非 public 方法上的 @Transaction 註解。
若 @Transaction 註解在類上,則表示該類上所有的方法均將在執行時織入事務。

(2)@Transactional註解失效情況

  • 沒有被 Spring 容器管理
    如果此時把 @Service 註解註釋掉,這個類就不會被載入成一個 Bean,那這個類就不會被 Spring 管理了,事務自然就失效了。
// @Service
public class OrderServiceImpl implements OrderService {
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
}
  • 用在非public修飾的方法
    @Transactional是基於動態代理的,Spring的代理工廠在啟動時會掃描所有的類和方法,並檢查方法的修飾符是否為public,非public時不會獲取@Transactional的屬性資訊,這時@Transactional的動態代理物件為空。
  • 多執行緒呼叫
    Spring實現事務的原理是透過ThreadLocal把資料庫連線繫結到當前執行緒中,同一個事務中資料庫操作使用同一個jdbc connection,新開啟的執行緒獲取不到當前jdbc connection
  • 自身呼叫自身方法
@Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void update(Order order) {
        updateOrder(order);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateOrder(Order order) {
        // update order
    }   
}

上面的不生效
解決的方案:
[1]注入BeanFactory容器,使用容器獲取代理類,然後使用代理類呼叫事務方法
image
[2]使用@Autowired註解或者@Resource註解注入自己,然後使用注入的代理類去呼叫事務方法
image
[3]類上面新增註解@EnableAspectJAutoProxy(exposeProxy = true),然後在類中獲取當前類的代理類,使用代理類呼叫事務方法。如果不新增註解的話,可能會出現錯誤
image

  • 異常被catch“吃了”導致@Transactional失效

  • @Transactional 註解屬性 rollbackFor 異常型別設定錯誤,資料庫不支援事務

2、手動啟用事務

(1)使用手動事務的場景
1、非同步處理業務時,此時業務的事務已經脫離正常的aop機制了,所以需要手動提交事務,來保持業務中多個事務的一致性。
2、線上程中我們也有可能需要事務,這個事務可以使用手動事務。
(2)使用

    @Autowired
    PlatformTransactionManager platformTransactionManager;
    @Test
    void testTransactional(){
        // 獲取事務
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transaction = platformTransactionManager.getTransaction(defaultTransactionDefinition);

        // 手動事務
        User user = new User();
        user.setId(13L);
        user.setOpenId("123456");
        user.setUsername("ccgg");
        userRepository.save(user);
        // 事務提交
        platformTransactionManager.commit(transaction);
        try{
            int a = 1/0;
        }catch (Exception e){
            // 事務回滾
            platformTransactionManager.rollback(transaction);
        }
    }

注意:這裡引入 PlatformTransactionManager 而不是DataSourceTransactionManager。springboot根據資料庫jpa配置自動實現PlatformTransactionManager。資料庫元件如果是spring-boot-starter-jdbc,框架會預設注入DataSourceTransactionManager

參考:https://blog.csdn.net/weixin_37862824/article/details/124333511

3、事務管理器

專案中資料庫元件如果是spring-boot-starter-jdbc,框架會預設注入DataSourceTransactionManager
如果你新增的是spring-boot-starter-data-jpa依賴,框架會預設注入JpaTransactionManager例項。

JPA的事務管理器和JDBC的事務管理器都實現了PlatformTransactionManager介面

測試專案中使用的是什麼型別的事務

//引數platformTransactionManager spring會預設幫我們住進來
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
    System.out.println(platformTransactionManager.getClass().getName());
    return new Object();
}

(1)自定義事務型別
設定Spring的事務型別是DataSourceTransactionManager

@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);//其中dataSource框架會自動為我們注入
}

在Spring容器中,我們手工註解@Bean 將被優先載入,框架不會重新例項化其他的PlatformTransactionManager實現類

@Configuration
public class Profiledemo implements TransactionManagementConfigurer {

	//注入事務管理器2
    @Resource(name="txManager2")
    private PlatformTransactionManager txManager2;

    //建立事務管理器2
    @Bean(name = "txManager2")
    public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
        return new JpaTransactionManager(factory);
    }

    //建立事務管理器1
    @Bean(name = "txManager1")
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    //實現介面TransactionManagementConfigurer方法,其返回值代表在擁有多個事務管理器的情況下預設使用的事務管理器
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager2;
    }
}

Service使用中指定事務

public class Service{

	//使用value具體指定使用哪個事務管理器
	@Transactional(value="txManager1")
	@Override
	public void xxx() {
	    
	}

	// 在存在多個事務管理器的情況下,預設使用上面annotationDrivenTransactionManager()方法返回的事務管理器
	@Transactional
	public void xxx() {
	    
	}
}

注意:多個資料多個事務管理器,如果沒有重寫annotationDrivenTransactionManager方法,並且Transactional沒有指定value,啟動是會報錯的

相關文章