JPA 實體生命週期理解和總結(轉)
(1)前言:
最近在使用Spring Data Jpa開發的時候,發現更新單個物件的時候,可以這麼寫:
@Transactional(value = "oracleTM")
public E update(Serializable id, E newEntity) {
E entity = getEntity(id);
BeanUtils.copyProperties(newEntity, entity);
return entity;
}
沒有寫任何Update的語句,竟然就自動執行重新整理了
(2)Spring Data Jpa實體狀態分析
圖中虛線框代表在持久化上下文中,所謂在持久化上下文的意思就是一旦該實體物件處於該環境中的時候,那麼此實體的屬性發生了任何的改變都會同步到資料庫中,無需再自己手工呼叫管理器的方法,該記錄會自動同步,這些操作是由AbstractFlushingEventListener完成。
(3)Spring Data Jpa更新分析
通過AbstractFlushingEventListener執行更新
//DAO層
public Z getEntity(Serializable id) {
//獲取出來的資料是託管狀態
return (Z) entityManager.find(getEntityClass(), new Long(id.toString()));
}
//SERVICE層
@Transactional(value = "oracleTM", readOnly = true)
public E getEntity(Serializable id) {
return getDao().getEntity(id);
}
@Transactional(value = "oracleTM")
public E update(Serializable id, E newEntity) {
E entity = getEntity(id);//託管狀態
BeanUtils.copyProperties(newEntity, entity);//託管中的資料發生變化
return entity;
}
後臺處理日誌:
DEBUG: [org.hibernate.event.internal.AbstractFlushingEventListener.(143)prepareEntityFlushes] - Processing flush-time cascades
DEBUG: [org.hibernate.event.internal.AbstractFlushingEventListener.(184)prepareCollectionFlushes] - Dirty checking collections
DEBUG: [org.hibernate.event.internal.AbstractFlushingEventListener.(117)logFlushResults] - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
(4)Spring Data Jpa 實體狀態詳解
瞬時狀態:
實際上就是new了一個普通的JavaBean物件。
託管狀態:
1.當1.瞬時物件呼叫了管理器的persist()後,即可將一般的JavaBean做為了持久Bean,該Bean的任何屬性改動都會牽涉到資料庫記錄的改動。2.一旦該記錄flush到資料庫之後,並且事務提交了,那麼此物件不在持久化上下文中,即:變為了遊離(沒人管的孩子)狀態了。在遊離狀態的時候呼叫更新、重新整理方法後,遊離狀態物件就變為了在持久化上下文的託管狀態了。3.通過管理器的find方法,將實體從資料庫查詢出來後,該實體也就變為了託管形態。
持久化狀態:
當處在託管狀態的實體Bean被管理器flush了,那麼就在極短暫的時間進入了持久化狀態,事務提交之後,立刻變為了遊離狀態。您可以把持久化狀態當做實實在在的資料庫記錄。
遊離狀態:
遊離狀態就是提交到資料庫後,事務commit後實體的狀態,因為事務已經提交了,此時實體的屬性任你如何改變,也不會同步到資料庫,因為遊離是沒人管的孩子,不在持久化上下文中。
銷燬物件:
一般要刪除一個持久化物件的時候都是先find出來,之後呼叫remove方法刪之,此時這個物件就是銷燬物件,實際上就是瞬時物件的另一種形態罷了。
(5)Spring Data Jpa 應用分析
1.實體管理器高階操作——getReference()
用於查詢單記錄實體,和find相似
程式碼如下
// 載入一個實體 T entity = entityManager.getReference(entityClass, id); |
它與find的區別就是:當根據主鍵查詢記錄不存在的時候,將丟擲異常EntityNotFoundException。這樣我們就可以捕獲異常後做一些自己的處理。
2.實體管理器高階操作——提交方式FlushModeType
提交(呼叫flush)分為2種方式:
AUTO:自動提交,實體管理器會在適當的時機同步實際記錄到資料庫,也是預設的提交方式。
COMMIT:一旦一個事務完畢了,那麼就立刻提交到資料庫(忽略事務共享、事務傳播)。
很多人建議使用預設的AUTO。
3.大量資料分批提交
有的時候我們需要迴圈儲存資料,當儲存大量資料的時候,如果到最後才提交所有資料,那麼資料庫的負載可能會比較大。我們可以這樣做,每30個記錄就提交(flush)一次。程式碼如下:
public void updateBatch(List<Z> list) {
for (int i = 0; i < list.size(); i++) {
entityManager.merge(list.get(i)); //變成託管狀態
if (i % 30 == 0) {
entityManager.flush(); //變成持久化狀態
entityManager.clear(); //變成遊離狀態
}
}
}
public void saveBatch(List<Z> list) {
for (int i = 0; i < list.size(); i++) {
entityManager.persist(list.get(i)); //變成託管狀態
if (i % 30 == 0) {
entityManager.flush(); //變成持久化狀態
entityManager.clear(); //變成遊離狀態
}
}
}
每到30條記錄的時候就強制提交。
4.refresh()
該方法是和flush()相反,是將資料庫記錄重新讀到實體中,這樣實體也是出於持久化環境中了,處於託管狀態。
5.clear()
該方法是將所有的處於上下文中的實體全部轉換成遊離狀態,此時還沒有及時flush到資料庫的資訊,很遺憾,將不會持久化到資料庫中。不是急於釋放資源的情況下,請慎用之。