[每日短篇] 19 - Spring Data JPA 的 @Modifying 注解需要注意的問題?
JPA 的 Repository 提供一種非常易用的機制用于 ORM 方式處理數(shù)據(jù)十籍,但是如果需要一次性更新一批數(shù)據(jù)的部分字段别垮,構(gòu)造所有實體并逐個修改字段再存回數(shù)據(jù)庫就顯得有些臃腫。在 JPA 中提供了?@Query?注解用于使用 JPQL 執(zhí)行數(shù)據(jù)庫操作椒袍,如果數(shù)據(jù)庫操作是修改數(shù)據(jù)而非查詢數(shù)據(jù)藕施,則需要再額外使用?@Modifying?注解提示 JPA 該操作是修改操作寇损。
當(dāng)進行 find 操作時,JPA 在 EntityManager 中緩存了 find 生成的對象铅碍,當(dāng)再次 find 時會直接返回該對象琢融。于是可能會出現(xiàn)下面這種情況 用?@Query?定義一個修改狀態(tài)的方法
public interface EntityRepository extends JpaRepository {
????@Modifying
????@Query("update Entity set status = 'IGNORED' where id = ?1")
? ? intupdateStatus(intid);
}
先讀取一個對象老充,再修改對象狀態(tài),再次讀取對象
Optional entityBefore = repository.findById(1);
repository.updateStatus(1);
Optional entityAfter = repository.findById(1);
結(jié)果會發(fā)現(xiàn) entityBefore 和 entityAfter 中的 Entity 對象 id 是相同的肖油,中間對狀態(tài)的修改并沒有體現(xiàn)出來憨愉!當(dāng)然烦绳,其原因也很明確,@Query?跟 find 和 save 系列方法是兩套不同的體系配紫,@Query?引起的數(shù)據(jù)庫變更 EntityManager 并不能發(fā)現(xiàn)径密,更進一步說,使用其它工具或者其它框架修改數(shù)據(jù)庫中的數(shù)據(jù)躺孝,也不能及時反應(yīng)到 JPA 的 find 系列方法上來享扔。
當(dāng)然底桂,只要有緩存機制就一定不可避免存在此類問題,這僅是個取舍問題而不要認(rèn)為是 BUG惧眠。如果要解決 find 得到的值不是數(shù)據(jù)庫中最新值的問題可以有幾種方式籽懦,避免使用?@Query?是一種方式,在需要時顯式清理 EntityManager 的緩存也是一種方式氛魁。Spring Data JPA 提供了另外一種方式則是?@Modifying(clearAutomatically = true)暮顺,@Modifying?的 clearAutomatically 屬性為 true 時,執(zhí)行完 modifying query 之后就會清理緩存秀存,從而在下次 find 時就可以讀取到數(shù)據(jù)庫中的最新值捶码。
自動清理之后還會帶來一個新的問題,clear 操作清理的緩存中或链,還包括提交后未 flush 的數(shù)據(jù)惫恼,例如調(diào)用 save 而不是 saveAndFlush 就有可能不會立即將修改內(nèi)容更新到數(shù)據(jù)庫中,在 save 之后 flush 之前調(diào)用?@Modifying(clearAutomatically = true)?修飾的方法就有可能導(dǎo)致修改丟失株扛。如果再要解決這個問題尤筐,還可以再加上另外一個屬性?@Modifying(clearAutomatically = true, flushAutomatically = true),@Modifying?的 flushAutomatically 屬性為 true 時洞就,執(zhí)行 modifying query 之前會先調(diào)用 flush 操作盆繁,從而避免數(shù)據(jù)丟失問題。
在實際運行中旬蟋,clear 和 flush 操作都可能需要消耗一定的時間油昂,要根據(jù)系統(tǒng)實際情況可以選擇使用其中的一個或兩個屬性,以保證系統(tǒng)的正確性倾贰。
參考: flushAutomatically 屬性是在?https://jira.spring.io/browse/DATAJPA-806?提出并被采納的冕碟。