[每日短篇] 19 - Spring Data JPA 的 @Modifying 註解須要注意的問題

JPA 的 Repository 提供一種很是易用的機制用於 ORM 方式處理數據,可是若是須要一次性更新一批數據的部分字段,構造全部實體並逐個修改字段再存回數據庫就顯得有些臃腫。在 JPA 中提供了 @Query 註解用於使用 JPQL 執行數據庫操做,若是數據庫操做是修改數據而非查詢數據,則須要再額外使用 @Modifying 註解提示 JPA 該操做是修改操做。spring

當進行 find 操做時,JPA 在 EntityManager 中緩存了 find 生成的對象,當再次 find 時會直接返回該對象。因而可能會出現下面這種狀況 用 @Query 定義一個修改狀態的方法數據庫

public interface EntityRepository extends JpaRepository<Entity, Integer> {

    @Modifying
    @Query("update Entity set status = 'IGNORED' where id = ?1")
    int updateStatus(int id);

}

先讀取一個對象,再修改對象狀態,再次讀取對象緩存

Optional<Entity> entityBefore = repository.findById(1);

repository.updateStatus(1);

Optional<Entity> entityAfter = repository.findById(1);

結果會發現 entityBefore 和 entityAfter 中的 Entity 對象 id 是相同的,中間對狀態的修改並無體現出來!固然,其緣由也很明確,@Query 跟 find 和 save 系列方法是兩套不一樣的體系,@Query 引發的數據庫變動 EntityManager 並不能發現,更進一步說,使用其它工具或者其它框架修改數據庫中的數據,也不能及時反應到 JPA 的 find 系列方法上來。框架

固然,只要有緩存機制就必定不可避免存在此類問題,這僅是個取捨問題而不要認爲是 BUG。若是要解決 find 獲得的值不是數據庫中最新值的問題能夠有幾種方式,避免使用 @Query 是一種方式,在須要時顯式清理 EntityManager 的緩存也是一種方式。Spring Data JPA 提供了另一種方式則是 @Modifying(clearAutomatically = true)@Modifying 的 clearAutomatically 屬性爲 true 時,執行完 modifying query 以後就會清理緩存,從而在下次 find 時就能夠讀取到數據庫中的最新值。工具

自動清理以後還會帶來一個新的問題,clear 操做清理的緩存中,還包括提交後未 flush 的數據,例如調用 save 而不是 saveAndFlush 就有可能不會當即將修改內容更新到數據庫中,在 save 以後 flush 以前調用 @Modifying(clearAutomatically = true) 修飾的方法就有可能致使修改丟失。若是再要解決這個問題,還能夠再加上另一個屬性 @Modifying(clearAutomatically = true, flushAutomatically = true)@Modifying 的 flushAutomatically 屬性爲 true 時,執行 modifying query 以前會先調用 flush 操做,從而避免數據丟失問題。code

在實際運行中,clear 和 flush 操做均可能須要消耗必定的時間,要根據系統實際狀況能夠選擇使用其中的一個或兩個屬性,以保證系統的正確性。對象

參考: flushAutomatically 屬性是在 https://jira.spring.io/browse/DATAJPA-806 提出並被採納的。get

相關文章
相關標籤/搜索