1. 程式人生 > >JPA(Hibernate)實體狀態

JPA(Hibernate)實體狀態

一句話總結:通過JPA(Hibernate)對實體物件進行增刪改查時,JPA(Hibernate)要維護即儲存物件在Session即Hibernate一級快取中,同時還要維護其狀態,具體狀態變化如下圖所示:

通常我們無需關心JPA(Hibernate)的實體狀態,但是碰到一些問題時需要對實體狀態有所瞭解。

問題一:Cannot remove detached entity異常

通常我們會通過Spring配置自動事務,此時若在北向呼叫ServiceA查詢出一個物件,這裡查詢也會有事務提交,那麼這個物件其實處於detached狀態,北向再呼叫ServiceB傳遞此物件進行刪除,就會報錯提示cannot remove detached entity異常。還有一個場景是批量提交場景,網上的示例通常在批量提交後會呼叫em.clear來釋放記憶體,此時該物件也處於detached狀態,再remove同樣會報錯。

northbound method() {
    Object obj = service.findObject(id);
    service.removeObject(obj);  
}

service findObject method() {
    // Spring事務,自動提交commit
    return object;
}

service removeObject method(Object obj) {
    // 這裡會報錯
    JpaEntityManager.remove(obj);
}

 問題二:瞭解實體狀態後,我們知道事務自動提交後再操作物件會有detach異常,那麼設計架構時就要確保每次REST請求時使用不同的Hibernate Session,若併發多次請求使用同一Session裡快取的物件,就得手動去維護物件的狀態才能防止出現detach異常。

通常我們還會使用Spring IOC,注入單例物件,這會讓我們覺得Hibernate Session是同一個,如下:

class Northbound {
    @Autowired
    Service service;
}

class Service {
    @PersistenceContext
    EntityManager em;
}

但其實Spring已經幫我們隱含處理了,這裡有點繞,Northbound注入Service時是單例的,每次呼叫的是同一個service物件。但Service注入EntityManager時,卻會根據每個執行緒即每次REST請求注入新的EntityManager(其下封裝Hibernate Session)。Spring會針對PersistenceContext註解做特殊處理,將EntityManager(Session)與執行緒繫結,儲存在ThreadLocal中。在OSGI環境中,還會使用OSGI Coordinate機制。所以用斷點除錯去檢視,每次請求的EntityManager物件都是不同的。

引用:

https://docs.oracle.com/cd/E16439_01/doc.1013/e13981/undejbs003.htm

https://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_em_lifecycle.html

https://stackoverflow.com/questions/42074270/should-there-be-an-entitymanager-per-thread-in-spring-hibernate