2009-10-22 49 views
83

Tôi đã gặp một tình huống (mà tôi nghĩ là lạ nhưng có thể là khá bình thường) khi tôi sử dụng EntityManager.getReference (LObj.getClass(), LObj .getId()) để có được một thực thể cơ sở dữ liệu và sau đó vượt qua đối tượng trả về để được tiếp tục tồn tại trong một bảng khác.Khi nào sử dụng EntityManager.find() vs EntityManager.getReference()

Vì vậy, về cơ bản các dòng chảy là như thế này:

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

tôi đã nhận được ngoại lệ sau "java.lang.IllegalArgumentException: thực thể Unknown: com.my.persistence.L $$ $$ EnhancerByCGLIB 3e7987d0"

Sau khi xem xét nó một lúc, cuối cùng tôi đã tìm ra rằng đó là vì tôi đã sử dụng phương thức EntityManager.getReference() mà tôi đã nhận được ngoại lệ trên vì phương thức đã trả về proxy.

Điều này khiến tôi tự hỏi, khi nào nên sử dụng phương thức EntityManager.getReference() thay vì phương thức EntityManager.find()?

EntityManager.getReference() ném ra một EntityNotFoundException nếu nó không thể tìm thấy thực thể đang được tìm kiếm mà rất thuận tiện trong chính nó. Phương thức EntityManager.find() chỉ trả về null nếu nó không thể tìm thấy thực thể.

Liên quan đến ranh giới giao dịch, âm thanh với tôi như bạn sẽ cần phải sử dụng phương thức find() trước khi chuyển thực thể mới được tìm thấy sang giao dịch mới. Nếu bạn sử dụng phương thức getReference() thì có thể bạn sẽ kết thúc trong một tình huống tương tự như của tôi với ngoại lệ trên.

+0

Quên đề cập đến, tôi đang sử dụng Hibernate làm nhà cung cấp JPA. – SibzTer

Trả lời

133

Tôi thường sử dụng phương pháp getReference khi tôi không cần truy cập trạng thái cơ sở dữ liệu (nghĩa là phương thức getter). Chỉ cần thay đổi trạng thái (ý tôi là phương thức setter). Như bạn đã biết, getReference trả về một đối tượng proxy sử dụng một tính năng mạnh được gọi là kiểm tra bẩn tự động. Giả sử sau

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

} 

Nếu tôi gọi tìm phương pháp, nhà cung cấp JPA, đằng sau hậu trường, sẽ gọi

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ? 

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

Nếu tôi gọi getReference phương pháp, nhà cung cấp JPA, đằng sau hậu trường, sẽ gọi

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

Và bạn biết tại sao ???

Khi bạn gọi getReference, bạn sẽ nhận được một đối tượng proxy. Một cái gì đó như thế này (nhà cung cấp JPA sẽ chăm sóc thực hiện proxy này)

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

} 

Vì vậy, trước khi giao dịch cam kết, nhà cung cấp JPA sẽ thấy stateChanged cờ để cập nhật HAY KHÔNG người thực thể. Nếu không có hàng nào được cập nhật sau khi câu lệnh cập nhật, nhà cung cấp JPA sẽ ném EntityNotFoundException theo đặc tả JPA.

regards,

+0

Tôi đang sử dụng EclipseLink 2.5.0 và các truy vấn được nêu ở trên không chính xác. Nó luôn luôn đưa ra một 'SELECT' trước' UPDATE', bất kể 'find()'/'getReference()' nào tôi sử dụng. Điều gì là tồi tệ hơn, 'SELECT' đi qua các mối quan hệ không-LAZY (phát hành mới' SELECTS'), mặc dù tôi chỉ muốn cập nhật một trường duy nhất trong một thực thể. –

+0

@Arthur Ronald điều gì sẽ xảy ra nếu có chú thích Phiên bản trong thực thể được gọi bởi getReference? –

+0

Tôi có cùng vấn đề với @DejanMilosevic: khi loại bỏ một thực thể thu được thông qua getReference(), một SELECT được phát hành trên thực thể đó và nó đi qua tất cả các mối quan hệ LAZY của thực thể đó, do đó phát hành nhiều SELECTS (với EclipseLink 2.5.0). –

5

Bởi vì một tài liệu tham khảo là 'quản lý', nhưng không ngậm nước, nó cũng có thể cho phép bạn gỡ bỏ một thực thể bởi ID, mà không cần phải tải nó vào bộ nhớ đầu tiên.

Vì bạn không thể loại bỏ một thực thể không được quản lý, chỉ đơn giản là ngớ ngẩn để tải tất cả các trường bằng cách sử dụng find (...) hoặc createQuery (...), chỉ để xóa ngay.

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId); 
em.remove(myObject); 
Các vấn đề liên quan