Hibernate 髒檢查機制如何運作
1. 簡介
在本教程中,我們將探索 Hibernate 的功能之一:髒檢查機制。
這個概念使 Hibernate 能夠**自動偵測實體狀態的變化,從而允許它自動更新實體。**我們首先會了解髒檢查、髒實體的概念以及機制的工作原理。然後,我們將繼續討論程式碼範例。最後,我們將討論如何控制並在必要時停用此功能。
2. Hibernate 中的髒檢查是什麼?
髒檢查是 Hibernate 中的一種機制,可自動偵測並同步對實體所做的更改,而無需明確更新查詢。當記憶體中的實體狀態發生變化時,Hibernate 會識別這些變化並確保在提交交易時將它們持久保存在資料庫中。
在深入探討該機制之前,讓我們先將髒實體定義為與其上次已知的資料庫狀態相比狀態發生了變化的實體。
值得注意的是,髒檢查僅適用於 Hibernate 實體生命週期中處於持久狀態的實體。換句話說,它僅適用於與活動會話相關聯的實體。
3. 髒檢查如何運作?
髒檢查機制會對最後已知的實體狀態進行快照。這通常發生在首次從資料庫載入持久性物件時。在會話期間,如果實體的任何屬性被修改,則該實體就會變髒。稍後,在刷新持久性上下文時,Hibernate 會根據先前建立的快照檢查與會話相關的每個髒實體,並在資料庫中更新它。
我們不需要明確地呼叫任何方法來保存資料。 Hibernate 在提交事務之前自動刷新會話,從而確保執行髒檢查。
整個過程是自動的。 Hibernate 建立 SQL 查詢並更新資料庫中對應的行。
3.1.性能影響
雖然髒檢查很方便,但它確實會帶來一些性能考慮和影響。
Hibernate 追蹤物件的變化,這會增加記憶體使用量和處理時間,尤其是對於大型資料集。在大多數情況下,開銷會很小,因為它已經針對效能進行了最佳化。
不過,有一項功能可以提高效能——增強髒追蹤。增強髒追蹤是一種最佳化,其中 Hibernate 使用字節碼增強來追蹤屬性層級的實體變化,而不是在刷新期間比較整個物件。如果沒有此增強功能,將會比較完整快照,但啟用此增強功能後,Hibernate 確切地知道哪些欄位發生了變化,並且只更新這些欄位。
為了啟用髒追蹤,我們需要在建置工具中包含並配置Hibernate 增強器插件。
4.程式碼範例
現在我們了解了該機制的工作原理,讓我們在實踐中看看它。
作為第一步,我們將建立一個基本的實體:
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private double price;
// omitted constructors, getters and setters
}
我們也新增一個儲存庫:
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
Optional<Product> findByCode(String code);
}
完成基本設定後,讓我們在持久性上下文中修改實體的狀態。為了實現這一點,我們首先創建一個實例並持久化它。之後,我們改變其狀態,刷新會話,並觀察無需額外方法呼叫即可應用的變更:
@Test
@Transactional
void givenProduct_whenModifiedWithoutSave_thenAssertChangesPersisted() {
Product product = new Product("LOREM", 100.00);
productRepository.save(product);
product.setPrice(80.00);
entityManager.flush();
entityManager.clear();
Product updatedProduct = productRepository.findByCode("LOREM").orElseThrow(RuntimeException::new);
assertEquals(80.00, updatedProduct.getPrice());
}
測試確認刷新後實體狀態確實發生了改變。當發生交易提交時,資料將更新到資料庫中的對應行,我們也可以在日誌中檢查:
Hibernate: insert into product (code,price,id) values (?,?,default)
Hibernate: update product set code=?,price=? where id=?
Hibernate: select p1_0.id,p1_0.code,p1_0.price from product p1_0 where p1_0.code=?
5. 禁用髒檢查機制
如同所觀察到的,髒檢查是完全自動化且有用的,但如果我們想停用它怎麼辦?不幸的是,由於髒檢查已深度整合到框架中,因此無法立即停用它。然而,在本節中,我們將討論髒檢查不適用的場景。
5.1.明確分離實體
如前所述,髒檢查僅適用於持久狀態的實體。一旦實體被手動分離(透過清除持久性上下文)或當交易結束時,Hibernate 將停止追蹤更改,從而阻止自動更新。
我們可以透過分離實體並斷言值保持不變來修改前面的測試:
@Test
@Transactional
void givenDetachedProduct_whenModifiedWithoutSave_thenAssertChangesNotPersisted() {
Product product = new Product("LOREM", 100.00);
productRepository.save(product);
entityManager.detach(product);
product.setPrice(80.00);
entityManager.flush();
entityManager.clear();
Product updatedProduct = productRepository.findByCode("LOREM").orElseThrow(RuntimeException::new);
assertEquals(100.00, updatedProduct.getPrice());
}
5.2.只讀事務
當使用@Transactional(readOnly = true)
將交易標記為唯讀時,Hibernate 會透過跳過實體快照來最佳化效能。由於沒有建立快照,Hibernate 無法偵測到修改。當管理實體被修改時,如果沒有手動儲存,這些變更將被忽略。
一般來說, @Transactional(readOnly=true)
不會在資料庫層級強制執行唯讀行為,它只會阻止 Hibernate 拍攝快照、追蹤修改和自動刷新工作階段。如果呼叫明確的save()
或persist()
則將會套用變更。
5.3.與@Immutable
實體的交互
以@Immutable
註解的實體被 Hibernate 視為唯讀。由於它們被認為是不可改變的,因此 Hibernate 會跳過追蹤修改,從而防止髒檢查。即使實體的屬性被修改,Hibernate 也不會追蹤該變化,也不會向資料庫提交任何更新。
6. 結論
在本文中,我們探討了 Hibernate 中的髒檢查機制如何運作、何時應用以及如何自動將實體變更與資料庫同步。雖然髒檢查無法完全停用,但我們已經看到髒檢查不起作用的特定場景。
與往常一樣,完整的程式碼範例可在 GitHub 上找到。