SpringBoot 整合 MongoDB 進行事務操作
一、簡介
MongoDB 從 4.0 開始支援多文檔交易,即事務(Transactions),我們會先介紹 SpringBoot 操作 MongoDB 進行 CRUD 的2種方式: MongoRepository 和 MongoTemplate,接著說明如何透過簡單的程式碼和設定讓其具有事務(Transactions)的特性。
二、準備
1. 環境:
SpringBoot v2.4.8
MongoDB v4.4.6
2. SpringBoot 專案: 可透過 Spring Initializr(https://start.spring.io/) 來建立一個基本的 SpringBoot 專案,並加入兩個主要的 Dependencies: Spring Data MongoDB 和 Web,專案建立後在 build.gradle 裡可看到如下的依賴 lib:
3. MongoDB 連線相關設定: 設定在 application.properties 中,如下圖所示
注意: 使用 uri 的方式時,若userName 和 password 包含冒號(:)或at(@)符號時,需要將其URLEncoder編碼。關於 Application Properties 參數設定,可以參考 Spring Doc(https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html)。
4. 建立資料實體: 創建一個 User entity,如下:
三、CRUD操作方式
MongoRepository:
1. 建立數據訪問層 UserRepository: 如同使用 Spring Data JPA 的方式一樣,定義好客製的 Repository 後,就可以使用 JPA 提供的接口(方法)存取資料庫,建立範例如下:
這裡重點是客製化的 UserRepository 要繼承 MongoRepository這個介面,該介面定義了一些存取 MongoDB 的方法,介面的實作則是在 SimpleMongoRepository 這個 class上,對於class SimpleMongoRepository的說明是:
2. 建立 Controller: 利用前一步驟創建的 UserRepository 來操作 MongoDB 的 CRUD,如下圖所示:
3. 結果測試:
寫入資料:
查看DB:
MongoTemplate:
1. 建立數據訪問層的介面(UserDao)和其實作(UserDaoImpl):
UserDao:
UserDaoImpl: 要Autowired MongoTemplate
2. 建立 Controller:
3. 結果測試
寫入資料:
查看DB:
四、事務(Transactions)
MongoDB 從 4.0 開始支持多文件的事務交易(Multi-Document Transactions)以確保資料的完整性,本章節將會說明如何讓先前介紹的兩種 CRUD 操作具有事務的特性。
1. 添加 MongoTransactionManager 並將其註冊為 Bean: 可以添加在啟動類或是Config 類,擇一即可,如下
添加在啟動類(Application.java):
其中 TransactionOptions 可以根據 mongoDB的架構來設定 ReadPreference, ReadConcern 和 WriteConcern等讀寫操作的確認級別,上面的範例是以 sharded-cluster 來設定的。
添加在 Config 類:
2. 實作類添加@Transactional:可添加在 Service 或是 Dao的實作類
l MongoRepository:下面範例是添加在 Service的實作類
結果測試:
腳本: 欲將 userId: KFC123 的 age 從原本的20修改成30
我們執行程式並將斷點設定在第29行 return 之前,意味著程式還在 @Transactional的範圍內,也就是 DB的資料尚未更新,不過我們卻可以看到印出的資訊(save 完之後再去DB讀取)卻是已經變成更新後的30,這就表示我們在 Transaction 的設定上是無效的??
下圖是當前程式斷點停在第29行尚未執行回傳時的資料庫內容,age 的值依然是20。
其實從 Jepsen 對MongoDB 4.2.6 的測試報告(參考文件5)中就有提到: readConcern 為 snapshot 以及 writeConcern 為 majority 的設定下,執行 transaction 操作時在還沒對系統下達寫入的指令時竟然可以看到未來寫入的結果。上述的情形是 mongoDB 之後改版需要修正的地方,但以結果來看,我們的設定是有效的。
l MongoTemplate: 下面的範例是添加在 Dao的實作類
測試結果:
腳本: 欲將 userId: KFC123 的 age 從原本的30修改成18
同 Repository 的執行結果,雖然尚未 commit 之前就可以取到更新後的資料,但實際從DB來看,一樣是尚未更新前的數據。
五、結論
透過 SpringBoot的依賴和簡單設定我們可以操作 MongoDB 的 CRUD,並使其具有事務(Transactions)的特性,雖然目前 MongoDB 在事務的支持上並不完善(如我們上面的舉例,可以讀到尚未寫入到DB的值),但能肯定是 MongoDB 在水平擴展的這項特性上的突破,期待往後推出的版本對事務的支持可以更完備。
參考文件
1. https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#reference
2. https://spring.io/blog/2018/06/28/hands-on-mongodb-4-0-transactions-with-spring-data
3. https://www.baeldung.com/spring-data-mongodb-tutorial
4. https://www.baeldung.com/spring-data-mongodb-transactions
5. https://jepsen.io/analyses/mongodb-4.2.6,4.7. Read Your (Future) Writes