Hibernate 中的 load() 與 get()
一、簡介
在 Hibernate 中, load()
和get()
是用於從資料庫檢索資料的兩種方法。在本教程中,我們將探討這些方法之間的差異。
2. 載入策略
Hibernate 中的load()
方法採用延遲載入策略。呼叫時,它會傳回實體的代理對象,延遲資料庫查詢,直到存取該對象的屬性或方法。它的工作原理如下:
Person person = new Person("John Doe", 30);
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(person);
Person entity = session.load(Person.class, person.getId());
assertNotNull(entity);
assertEquals(person.getName(), entity.getName());
assertEquals(person.getAge(), entity.getAge());
首先,我們建立一個新的Person
物件並將其保存到資料庫中。然後,我們使用load()
來檢索具有已儲存的Person
id 的Person
實體。雖然該entity
看起來是Person
對象,但它是 Hibernate 提供的代理對象。
當我們存取代理物件的屬性(例如name
和age
時,Hibernate 會攔截這些調用,並在必要時從資料庫動態載入實際資料。相反, get()
方法採用急切載入策略,立即查詢資料庫並傳回實際的實體物件:
Person entity = session.get(Person.class, person.getId());
assertNotNull(entity);
assertEquals(person.getName(), entity.getName());
assertEquals(person.getAge(), entity.getAge());
3.數據存在時回傳值
當我們呼叫load()
方法時,Hibernate 使用提供的主鍵id
來建立實體的代理物件。此代理物件充當實體資料的佔位符,僅填入id
。實體的其餘屬性未初始化,首次存取時將從資料庫載入。如果我們嘗試存取代理物件的任何屬性而不初始化它,我們將得到一個LazyInitializationException
:
Session session = sessionFactory.openSession();
session = sessionFactory.openSession();
Person entity = session.load(Person.class, person.getId());
// Close the session
session.close();
assertThrows(LazyInitializationException.class, () -> {
entity.getName();
});
另一方面, get()
方法直接從資料庫中檢索實際的實體資料。這意味著get()
傳回的實體物件包含所有已初始化的屬性及其從資料庫中取得的實際值。因此,即使在 Hibernate 會話關閉後,我們仍然能夠毫無例外地存取entity
的屬性:
Session session = sessionFactory.openSession();
session = sessionFactory.openSession();
Person entity = session.get(Person.class, person.getId());
// Close the session
session.close();
// Access entity properties even after session closure
assertEquals(person.getName(), entity.getName());
assertEquals(person.getAge(), entity.getAge());
4. 數據不存在時的行為
當使用load()
時,它最初會傳回一個代理物件。實際的資料庫查詢被延後到我們存取物件的屬性為止。如果實體不存在,嘗試存取物件的屬性將導致ObjectNotFoundException
。因此,我們需要明確地處理這種情況:
Session session = sessionFactory.getCurrentSession();
Person entity = session.load(Person.class, 100L);
// Access properties of the entity, triggering the database query
assertThrows(ObjectNotFoundException.class, () -> {
entity.getName();
});
相反,當使用get()
方法時,如果實體存在於資料庫或快取中,它會立即檢索並傳回實際的實體物件。但是,如果entity
不存在, get()
會優雅地回傳null
:
Session session = sessionFactory.getCurrentSession();
Person entity = session.get(Person.class, 100L);
assertNull(entity);
5. 快取
load()
和get()
方法都利用一級快取來快取目前會話中檢索到的實體。它儲存當前會話中最近存取或操作的實體。如果該物件已經存在於快取中, get()
將立即傳回快取的物件。然而,即使資料可能被緩存, load()
仍然會傳回一個代理物件。下面是一個例子來說明這種行為:
Person person = new Person("John Doe", 30);
Session session = sessionFactory.openSession();
session.saveOrUpdate(person);
Person entity = session.get(Person.class, person.getId());
// Evict the entity from the session cache to simulate a new session
session.evict(entity);
Person cacheEntity = session.get(Person.class, person.getId());
當我們為會話建立一個Person
實體時,Hibernate 會快取該實體。隨後,我們呼叫第一個get()
方法。由於實體位於快取中,Hibernate 不會存取資料庫。然後,我們從會話緩存中逐出實體以模擬啟動新會話。
然後我們使用get()
方法再次檢索實體。這次,由於實體不在快取中,Hibernate 將存取資料庫。從輸出控制台我們可以看到Hibernate只印了一條SQL select
語句:
Hibernate: select p1_0.id,p1_0.age,p1_0.name from Person p1_0 where p1_0.id=?
六、總結
讓我們總結一下 Hibernate 中get()
和load()
方法之間的主要差異:
特徵 | get() |
load() |
---|---|---|
載入策略 | 預先載入(立即資料庫查詢) | 延遲載入(代理對象,存取時取得資料) |
傳回值(如果存在) | 實體 | 代理對象 |
資料庫查詢 | 立即執行 | 當訪問屬性時執行 |
傳回值(如果不存在) | 無效的 | 存取屬性時導致ObjectNotFoundException |
休眠緩存 | 從快取中檢索(如果存在) | 即使快取仍然返回代理 |
七、結論
在本文中,我們探討了 Hibernate 中get()
和load()
方法之間的根本差異。當我們需要立即存取物件並確保它是最新的時, get()
方法非常有用。另一方面,當我們只需要引用物件並優化資料庫呼叫時load()
方法很有用。
與往常一樣,範例的原始程式碼可在 GitHub 上取得。