日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
SpringBoot2.x基礎(chǔ)教程:使用JTA實(shí)現(xiàn)分布式事務(wù)

Spring Boot 2.x基礎(chǔ)教程:使用JTA實(shí)現(xiàn)分布式事務(wù)

作者:翟永超 2021-02-03 10:49:34

開發(fā)

開發(fā)工具

分布式 如果這兩條數(shù)據(jù)在一個數(shù)據(jù)庫中,那么通過之前介紹的事務(wù)管理就能輕松解決了。但是,當(dāng)這兩個操作位于不同的數(shù)據(jù)庫中,那么就無法實(shí)現(xiàn)了。

目前累計服務(wù)客戶近1000家,積累了豐富的產(chǎn)品開發(fā)及服務(wù)經(jīng)驗(yàn)。以網(wǎng)站設(shè)計水平和技術(shù)實(shí)力,樹立企業(yè)形象,為客戶提供網(wǎng)站設(shè)計制作、成都網(wǎng)站建設(shè)、網(wǎng)站策劃、網(wǎng)頁設(shè)計、網(wǎng)絡(luò)營銷、VI設(shè)計、網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。成都創(chuàng)新互聯(lián)始終以務(wù)實(shí)、誠信為根本,不斷創(chuàng)新和提高建站品質(zhì),通過對領(lǐng)先技術(shù)的掌握、對創(chuàng)意設(shè)計的研究、對客戶形象的視覺傳遞、對應(yīng)用系統(tǒng)的結(jié)合,為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進(jìn)步。

在一個Spring Boot項(xiàng)目中,連接多個數(shù)據(jù)源還是比較常見的。

當(dāng)我們采用多數(shù)據(jù)源的時候,同時也會出現(xiàn)一個這樣的特殊場景:我們希望對A數(shù)據(jù)源的更新和B數(shù)據(jù)源的更新具備事務(wù)性。這樣的例子很常見,比如:在訂單庫中創(chuàng)建一條訂單記錄,同時還需要在商品庫中扣減商品庫存。如果庫存扣減失敗,那么我們希望訂單創(chuàng)建也能夠回滾。

如果這兩條數(shù)據(jù)在一個數(shù)據(jù)庫中,那么通過之前介紹的事務(wù)管理就能輕松解決了。但是,當(dāng)這兩個操作位于不同的數(shù)據(jù)庫中,那么就無法實(shí)現(xiàn)了。

本文就來介紹一種解決這類問題的方法:JTA事務(wù)。

什么是JTA

JTA,全稱:Java Transaction API。JTA事務(wù)比JDBC事務(wù)更強(qiáng)大。一個JTA事務(wù)可以有多個參與者,而一個JDBC事務(wù)則被限定在一個單一的數(shù)據(jù)庫連接。所以,當(dāng)我們在同時操作多個數(shù)據(jù)庫的時候,使用JTA事務(wù)就可以彌補(bǔ)JDBC事務(wù)的不足。

在Spring Boot 2.x中,整合了這兩個JTA的實(shí)現(xiàn):

Atomikos:可以通過引入spring-boot-starter-jta-atomikos依賴來使用

Bitronix:可以通過引入spring-boot-starter-jta-bitronix依賴來使用

由于Bitronix自Spring Boot 2.3.0開始不推薦使用,所以在下面的動手環(huán)節(jié)中,我們將使用Atomikos作為例子來介紹JTA的使用。

動手試試

下面我們就來實(shí)操一下,如何在Spring Boot中使用JTA來實(shí)現(xiàn)多數(shù)據(jù)源下的事務(wù)管理。

準(zhǔn)備工作

  • 這里我們將使用最基礎(chǔ)的JdbcTemplate來實(shí)現(xiàn)數(shù)據(jù)訪問,所以如果你還不會使用JdbcTemplate配置多數(shù)據(jù)源,建議先看一下JdbcTemplate的多數(shù)據(jù)源配置。

場景設(shè)定:

  • 假設(shè)我們有兩個庫,分別為:test1和test2
  • 這兩個庫中都有一張User表,我們希望這兩張表中的數(shù)據(jù)是一致的
  • 假設(shè)這兩張表中都已經(jīng)有一條數(shù)據(jù):name=aaa,age=30;因?yàn)檫@兩張表中數(shù)據(jù)是一致的,所以要update的時候,就必須兩個庫中的User表更新時候,要么都成功,要么都失敗。

操作詳細(xì)

在pom.xml中加入JTA的實(shí)現(xiàn)Atomikos的Starter

  
 
 
 
  1.  
  2.     org.springframework.boot 
  3.     spring-boot-starter-jta-atomikos 
  4.  

在application.properties配置文件中配置兩個test1和test2數(shù)據(jù)源

  
 
 
 
  1. spring.jta.enabled=true 
  2.  
  3. spring.jta.atomikos.datasource.primary.xa-properties.url=jdbc:mysql://localhost:3306/test1 
  4. spring.jta.atomikos.datasource.primary.xa-properties.user=root 
  5. spring.jta.atomikos.datasource.primary.xa-properties.password=12345678 
  6. spring.jta.atomikos.datasource.primary.xa-data-source-class-name=com.mysql.cj.jdbc.MysqlXADataSource 
  7. spring.jta.atomikos.datasource.primary.unique-resource-name=test1 
  8. spring.jta.atomikos.datasource.primary.max-pool-size=25 
  9. spring.jta.atomikos.datasource.primary.min-pool-size=3 
  10. spring.jta.atomikos.datasource.primary.max-lifetime=20000 
  11. spring.jta.atomikos.datasource.primary.borrow-connection-timeout=10000 
  12.  
  13. spring.jta.atomikos.datasource.secondary.xa-properties.url=jdbc:mysql://localhost:3306/test2 
  14. spring.jta.atomikos.datasource.secondary.xa-properties.user=root 
  15. spring.jta.atomikos.datasource.secondary.xa-properties.password=12345678 
  16. spring.jta.atomikos.datasource.secondary.xa-data-source-class-name=com.mysql.cj.jdbc.MysqlXADataSource 
  17. spring.jta.atomikos.datasource.secondary.unique-resource-name=test2 
  18. spring.jta.atomikos.datasource.secondary.max-pool-size=25 
  19. spring.jta.atomikos.datasource.secondary.min-pool-size=3 
  20. spring.jta.atomikos.datasource.secondary.max-lifetime=20000 
  21. spring.jta.atomikos.datasource.secondary.borrow-connection-timeout=10000 

創(chuàng)建多數(shù)據(jù)源配置類

  
 
 
 
  1. @Configuration 
  2. public class DataSourceConfiguration { 
  3.  
  4.     @Primary 
  5.     @Bean 
  6.     @ConfigurationProperties(prefix = "spring.jta.atomikos.datasource.primary") 
  7.     public DataSource primaryDataSource() { 
  8.         return new AtomikosDataSourceBean(); 
  9.     } 
  10.  
  11.     @Bean 
  12.     @ConfigurationProperties(prefix = "spring.jta.atomikos.datasource.secondary") 
  13.     public DataSource secondaryDataSource() { 
  14.         return new AtomikosDataSourceBean(); 
  15.     } 
  16.  
  17.     @Bean 
  18.     public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource primaryDataSource) { 
  19.         return new JdbcTemplate(primaryDataSource); 
  20.     } 
  21.  
  22.     @Bean 
  23.     public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) { 
  24.         return new JdbcTemplate(secondaryDataSource); 
  25.     } 
  26.  

注意,這里除了家在的配置不同之外,DataSource也采用了AtomikosDataSourceBean注意與之前配置多數(shù)據(jù)源使用的配置和實(shí)現(xiàn)類的區(qū)別。

創(chuàng)建一個Service實(shí)現(xiàn),模擬兩種不同的情況。

  
 
 
 
  1. @Service 
  2. public class TestService { 
  3.  
  4.     private JdbcTemplate primaryJdbcTemplate; 
  5.     private JdbcTemplate secondaryJdbcTemplate; 
  6.  
  7.     public TestService(JdbcTemplate primaryJdbcTemplate, JdbcTemplate secondaryJdbcTemplate) { 
  8.         this.primaryJdbcTemplate = primaryJdbcTemplate; 
  9.         this.secondaryJdbcTemplate = secondaryJdbcTemplate; 
  10.     } 
  11.  
  12.     @Transactional 
  13.     public void tx() { 
  14.         // 修改test1庫中的數(shù)據(jù) 
  15.         primaryJdbcTemplate.update("update user set age = ? where name = ?", 30, "aaa"); 
  16.         // 修改test2庫中的數(shù)據(jù) 
  17.         secondaryJdbcTemplate.update("update user set age = ? where name = ?", 30, "aaa"); 
  18.     } 
  19.  
  20.     @Transactional 
  21.     public void tx2() { 
  22.         // 修改test1庫中的數(shù)據(jù) 
  23.         primaryJdbcTemplate.update("update user set age = ? where name = ?", 40, "aaa"); 
  24.         // 模擬:修改test2庫之前拋出異常 
  25.         throw new RuntimeException(); 
  26.     } 
  27.  

這里tx函數(shù),是兩句update操作,一般都會成功;而tx2函數(shù)中,我們?nèi)藶榈闹圃炝艘粋€異常,這個異常是在test1庫中的數(shù)據(jù)更新后才產(chǎn)生的,這樣就可以測試一下test1更新成功,之后是否還能在JTA的幫助下實(shí)現(xiàn)回滾。

創(chuàng)建測試類,編寫測試用例

  
 
 
 
  1. @SpringBootTest(classes = Chapter312Application.class) 
  2. public class Chapter312ApplicationTests { 
  3.  
  4.     @Autowired 
  5.     protected JdbcTemplate primaryJdbcTemplate; 
  6.     @Autowired 
  7.     protected JdbcTemplate secondaryJdbcTemplate; 
  8.  
  9.     @Autowired 
  10.     private TestService testService; 
  11.  
  12.     @Test 
  13.     public void test1() throws Exception { 
  14.         // 正確更新的情況 
  15.         testService.tx(); 
  16.         Assertions.assertEquals(30, primaryJdbcTemplate.queryForObject("select age from user where name=?", Integer.class, "aaa")); 
  17.         Assertions.assertEquals(30, secondaryJdbcTemplate.queryForObject("select age from user where name=?", Integer.class, "aaa")); 
  18.     } 
  19.  
  20.     @Test 
  21.     public void test2() throws Exception { 
  22.         // 更新失敗的情況 
  23.         try { 
  24.             testService.tx2(); 
  25.         } catch (Exception e) { 
  26.             e.printStackTrace(); 
  27.         } finally { 
  28.             // 部分更新失敗,test1中的更新應(yīng)該回滾 
  29.             Assertions.assertEquals(30, primaryJdbcTemplate.queryForObject("select age from user where name=?", Integer.class, "aaa")); 
  30.             Assertions.assertEquals(30, secondaryJdbcTemplate.queryForObject("select age from user where name=?", Integer.class, "aaa")); 
  31.         } 
  32.     } 
  33.  

這里有兩個測試用例:

  • test1:因?yàn)闆]有故意制造的異常,不出意外兩個庫的update都會成功,所以根據(jù)name=aaa去把兩個數(shù)據(jù)查出來,看age是否都被更新到了30。
  • test2:tx2函數(shù)會把test1中name=aaa的用戶age更新為40,然后拋出異常,JTA事務(wù)生效的話,會把a(bǔ)ge回滾回30,所以這里的檢查也是兩個庫的aaa用戶的age應(yīng)該都為30,這樣就意味著JTA事務(wù)生效,保證了test1和test2兩個庫中的User表數(shù)據(jù)更新一致,沒有制造出臟數(shù)據(jù)。

測試驗(yàn)證

將上面編寫的單元測試運(yùn)行起來:

 

觀察一下啟動階段的日志,可以看到這些Atomikos初始化日志輸出:

  
 
 
 
  1. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.default_max_wait_time_on_shutdown = 9223372036854775807 
  2. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.allow_subtransactions = true 
  3. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.recovery_delay = 10000 
  4. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.automatic_resource_registration = true 
  5. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.oltp_max_retries = 5 
  6. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.client_demarcation = false 
  7. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.threaded_2pc = false 
  8. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.serial_jta_transactions = true 
  9. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.log_base_dir = /Users/didi/Documents/GitHub/SpringBoot-Learning/2.x/chapter3-12/transaction-logs 
  10. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.rmi_export_class = none 
  11. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.max_actives = 50 
  12. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.checkpoint_interval = 500 
  13. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.enable_logging = true 
  14. 2021-02-02 19:00:36.145  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.log_base_name = tmlog 
  15. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.max_timeout = 300000 
  16. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.trust_client_tm = false 
  17. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: java.naming.factory.initial = com.sun.jndi.rmi.registry.RegistryContextFactory 
  18. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.tm_unique_name = 127.0.0.1.tm 
  19. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.forget_orphaned_log_entries_delay = 86400000 
  20. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.oltp_retry_interval = 10000 
  21. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: java.naming.provider.url = rmi://localhost:1099 
  22. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.force_shutdown_on_vm_exit = false 
  23. 2021-02-02 19:00:36.146  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.default_jta_timeout = 10000 
  24. 2021-02-02 19:00:36.147  INFO 8868 --- [           main] c.a.icatch.provider.imp.AssemblerImp     : Using default (local) logging and recovery... 
  25. 2021-02-02 19:00:36.184  INFO 8868 --- [           main] c.a.d.xa.XATransactionalResource         : test1: refreshed XAResource 
  26. 2021-02-02 19:00:36.203  INFO 8868 --- [           main] c.a.d.xa.XATransactionalResource     

同時,我們在transaction-logs目錄下,還能找到關(guān)于事務(wù)的日志信息:

  
 
 
 
  1. {"id":"127.0.0.1.tm161226409083100001","wasCommitted":true,"participants":[{"uri":"127.0.0.1.tm1","state":"COMMITTING","expires":1612264100801,"resourceName":"test1"},{"uri":"127.0.0.1.tm2","state":"COMMITTING","expires":1612264100801,"resourceName":"test2"}]} 
  2. {"id":"127.0.0.1.tm161226409083100001","wasCommitted":true,"participants":[{"uri":"127.0.0.1.tm1","state":"TERMINATED","expires":1612264100804,"resourceName":"test1"},{"uri":"127.0.0.1.tm2","state":"TERMINATED","expires":1612264100804,"resourceName":"test2"}]} 
  3. {"id":"127.0.0.1.tm161226409092800002","wasCommitted":false,"participants":[{"uri":"127.0.0.1.tm3","state":"TERMINATED","expires":1612264100832,"resourceName":"test1"}]} 

代碼示例

本文的相關(guān)例子可以查看下面?zhèn)}庫中的chapter3-12目錄:

Github:https://github.com/dyc87112/SpringBoot-Learning/

Gitee:https://gitee.com/didispace/SpringBoot-Learning/


標(biāo)題名稱:SpringBoot2.x基礎(chǔ)教程:使用JTA實(shí)現(xiàn)分布式事務(wù)
URL分享:http://m.5511xx.com/article/ccoijeg.html