2009-10-22 97 views
83

我碰到過一種情況(我認爲這很奇怪,但可能很正常),我使用EntityManager.getReference(LObj.getClass(),LObj .getId())獲取數據庫實體,然後將返回的對象傳遞到另一個表中。什麼時候使用EntityManager.find()vs EntityManager.getReference()

所以基本上流程是這樣的:

 
class TFacade{ 

    createT(FObj, AObj) { 
    T TObj = new T(); 
    TObj.setF(FObj); 
    TObj.setA(AObj); 
    ... 
    EntityManager.persist(TObj); 
    ... 
    L LObj = A.getL(); 
    FObj.setL(LObj); 
    FFacade.editF(FObj); 
    } 
} 

@TransactionAttributeType.REQUIRES_NEW 
class FFacade{ 

    editF(FObj){ 
    L LObj = FObj.getL(); 
    LObj = EntityManager.getReference(LObj.getClass(), LObj.getId()); 
    ... 
    EntityManager.merge(FObj); 
    ... 
    FLHFacade.create(FObj, LObj); 
    } 
} 

@TransactionAttributeType.REQUIRED 
class FLHFacade{ 

    createFLH(FObj, LObj){ 
    FLH FLHObj = new FLH(); 
    FLHObj.setF(FObj); 
    FLHObj.setL(LObj); 
    .... 
    EntityManager.persist(FLHObj); 
    ... 
    } 
} 

我得到以下異常 「java.lang.IllegalArgumentException異常:未知的實體:com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0」

經過一段時間的研究,我終於明白,這是因爲我正在使用EntityManager.getReference()方法,因爲該方法正在返回代理,所以我得到了上述異常。

這讓我想知道,什麼時候建議使用EntityManager.getReference()方法而不是EntityManager.find()方法

EntityManager.getReference()拋出一個EntityNotFoundException,如果它無法找到正在搜索的實體,這本身非常方便。如果EntityManager.find()方法找不到該實體,則它僅返回null。

關於事務邊界,對於我來說聽起來像你需要在將新發現的實體傳遞給新事務之前使用find()方法。如果你使用getReference()方法,那麼你可能會以類似於我的情況結束上述例外。

+0

忘了提及,我使用Hibernate作爲JPA提供程序。 – SibzTer 2009-10-22 14:29:56

回答

133

我通常使用getReference方法當我不需要訪問數據庫狀態(我的意思是getter方法)。只是爲了改變狀態(我是指setter方法)。正如你應該知道的那樣,getReference返回一個代理對象,它使用一個稱爲自動髒檢查的強大功能。假設有下面

public class Person { 

    private String name; 
    private Integer age; 

} 


public class PersonServiceImpl implements PersonService { 

    public void changeAge(Integer personId, Integer newAge) { 
     Person person = em.getReference(Person.class, personId); 

     // person is a proxy 
     person.setAge(newAge); 
    } 

} 

如果我叫找到方法,JPA提供商,在幕後,將調用

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ? 

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

如果我叫getReference方法,JPA提供商,在幕後,將致電

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

你知道爲什麼嗎?

當你調用getReference時,你會得到一個代理對象。像這樣的東西(JPA提供商負責實施這個代理)

public class PersonProxy { 

    // JPA provider sets up this field when you call getReference 
    private Integer personId; 

    private String query = "UPDATE PERSON SET "; 

    private boolean stateChanged = false; 

    public void setAge(Integer newAge) { 
     stateChanged = true; 

     query += query + "AGE = " + newAge; 
    } 

} 

事務提交此之前,JPA提供者會看到stateChanged標誌,以更新OR NOT人實體。如果update語句後沒有更新行,則JPA提供程序將根據JPA規範拋出EntityNotFoundException。

問候,

+0

我正在使用EclipseLink 2.5.0,並且上述查詢不正確。無論使用哪個find()'/'getReference()',它總是在'UPDATE'之前發出'SELECT'。更糟糕的是,'SELECT'遍歷非懶惰關係(發佈新的'SELECTS'),儘管我只想更新一個實體中的單個字段。 – 2014-02-01 16:22:10

+0

@Arthur Ronald如果在由getReference調用的實體中存在版本註釋,會發生什麼情況? – 2016-03-25 13:28:31

+0

我和@DejanMilosevic有同樣的問題:當刪除通過getReference()獲得的實體時,在該實體上發出一個SELECT,並且它遍歷該實體的所有LAZY關係,從而發出很多SELECTS(使用EclipseLink 2.5.0)。 – 2017-08-04 14:36:45

5

因爲參考「管理」,而不是保溼,還可以讓你通過ID刪除實體,而無需首先加載到內存中。

由於無法刪除非託管實體,因此只能使用find(...)或createQuery(...)加載所有字段,僅僅是爲了立即刪除它,這實在是愚蠢至極。

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId); 
em.remove(myObject); 
相關問題