2009-10-22 14 views
83

Natknąłem się na sytuację (która, jak myślę, jest dziwna, ale jest prawdopodobnie całkiem normalna), w której używam EntityManager.getReference (LObj.getClass(), LObj .getId()), aby uzyskać jednostkę bazy danych, a następnie przekazać zwracany obiekt do utrwalenia w innej tabeli.Kiedy używać EntityManager.find() kontra EntityManager.getReference()

Więc w zasadzie przepływ było tak:

 
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); 
    ... 
    } 
} 

ja otrzymuję następujący wyjątek "java.lang.IllegalArgumentException: nieznany podmiot: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0"

Po obejrzeniu go przez chwilę, w końcu zorientowałem się, że to dlatego, że używałem metody EntityManager.getReference(), że otrzymałem powyższy wyjątek, ponieważ metoda zwracała proxy.

Zastanawia mnie, , kiedy należy używać metody EntityManager.getReference() zamiast metody EntityManager.find()?

EntityManager.getReference() generuje wyjątek EntityNotFoundException, jeśli nie może znaleźć poszukiwanego obiektu, co jest bardzo wygodne samo w sobie. Metoda EntityManager.find() zwraca tylko wartość null, jeśli nie może znaleźć elementu.

Jeśli chodzi o granice transakcji, dźwięki brzmią tak, jakbyś musiał użyć metody find() przed przekazaniem nowo znalezionego obiektu do nowej transakcji. Jeśli użyjesz metody getReference(), prawdopodobnie skończysz w sytuacji podobnej do mojej z powyższym wyjątkiem.

+0

Zapomniałem wspomnieć, że używam Hibernate jako dostawcy JPA. – SibzTer

Odpowiedz

133

Używam zazwyczaj metody getReference, gdy nie potrzebuję dostępu do stanu bazy danych (mam na myśli metodę gettera). Aby zmienić stan (mam na myśli metodę setera). Jak powinieneś wiedzieć, getReference zwraca obiekt proxy, który używa potężnej funkcji zwanej automatycznym sprawdzaniem brudu. Załóżmy, że po

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); 
    } 

} 

jeśli zadzwonię znaleźć metodę dostawcę JPA, za kulisami, wezwie

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ? 

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

jeśli zadzwonię getReference metodę dostawcę JPA, za kulisami, będzie zadzwoń pod numer

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

A wiesz dlaczego ???

Po wywołaniu getReference otrzymasz obiekt proxy. Coś jak ten (dostawcy JPA dba o realizację tego proxy)

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; 
    } 

} 

Więc zanim zdecydują transakcję, dostawca JPA zobaczy stateChanged flagę w celu aktualizacji czy nie Osoba. Jeśli po aktualizacji nie zostaną zaktualizowane żadne wiersze, dostawca JPA wyrzuci EntityNotFoundException zgodnie ze specyfikacją JPA.

pozdrowienia,

+0

Używam EclipseLink 2.5.0, a powyższe zapytania są nieprawidłowe. Zawsze wydaje 'SELECT' przed' UPDATE', bez względu na to, którego 'z' find() '/' getReference() 'ja używam. Co gorsza, "SELECT" przemierza relacje NON-LAZY (wydając nowe "SELEKTY"), chociaż chcę tylko zaktualizować jedno pole w jednej jednostce. –

+0

@Arthur Ronald co się stanie, jeśli w obiekcie nazywanym przez getReference znajduje się adnotacja dotycząca wersji? –

+0

Mam taki sam problem jak @DejanMilosevic: podczas usuwania encji uzyskanej przez getReference(), SELECT jest wydawane na tej encji i przechodzi przez wszystkie LAZY relacje tego obiektu, w ten sposób wydając wiele SELECTS (z EclipseLink 2.5.0). –

5

Ponieważ odniesienia „udało”, ale nie nawilżona, może również pozwalają usunąć jednostkę przez ID, bez konieczności załadowania go do pamięci jako pierwszy.

Ponieważ nie można usunąć niezarządzanego obiektu, po prostu głupio jest załadować wszystkie pola za pomocą funkcji find (...) lub createQuery (...), aby natychmiast je usunąć.

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId); 
em.remove(myObject); 
Powiązane problemy