2011-03-23 44 views
2

我有使用JSF 2,JPA 2和EJB 3.1在GlassFish v3的運行(因此它使用的EclipseLink 2)的應用程序的1到n的關係的「另一側」插入的值。此應用程序有兩個JPA實體,PersonMessage,每個Message都有兩個Person s的參考,即消息發送方和消息接收方。類別Person具有提供對發送或接收的訪問的屬性Message。該Message類是這樣的:中的在JPA(使用的EclipseLink)

@Entity 
public class Message { 
    @ManyToOne 
    @JoinColumn(name="sender") 
    private Person sender; 

    @ManyToOne 
    @JoinColumn(name="receiver") 
    private Person receiver; 
    // ... some stuff ... 
} 

Person類是類似以下:

@Entity 
public class Person { 
    @OneToMany(fetch=FetchType.EAGER, mappedBy="sender") 
    private List<Message> sentMessages; 

    @OneToMany(fetch=FetchType.EAGER, mappedBy="receiver") 
    private List<Message> receivedMessages; 
    // ... more stuff ... 
} 

Message情況下,通過在DAO類中的方法創建:

@Stateless 
public class MessageDAOImpl implements MessageDAO { 
    public Message crete(Person sender, Person receiver) { 
     Message message = new Message(); 
     message.setSender(sender); 
     message.setReceiver(receiver); 
     entityManager.persist(message); 
     // Puting in other objects, hoping it will work 
     sender.getSentMessages().add(message); 
     receiver.getReceivedMessages().add(message); 

     // Saving sender and receiver: here comes the interesting part 
     entityManager.merge(sender); // [1] 
     entityManager.merge(receiver); // [2] 
     return message; 
    } 
} 

但是,一些驚人的事情發生。當我註釋掉線標記[1][2]並調用該方法,該消息不會出現在sender的發送消息和receiver的收到的郵件列表,當我在其他地方使用這些實體,即使它們是從實體管理器檢索(如通過EntityManager.find())。如果我取消註釋,那麼該消息就像預期的那樣出現在列表中。

我覺得有趣,因爲我設想的情況下,兩種情況下,當行註釋掉:

  • senderreceiver實體直接從數據庫中檢索,消息將出現在他們的名單,因爲我保存了該消息,因此該關係被保存在數據庫中;或
  • senderreceiver實體從一些高速緩存中檢索,所以他們已經添加到列表中的消息。

顯然,它不以這種方式工作。

我發現這awesome article關於Hibernate的緩存。基於它,我認爲問題在於,由於緩存不是對象而是值,因此需要使用EntityManager.merge()方法來刷新具有緩存值的實體。

是這樣嗎?如果不是,這種行爲的原因是什麼?撥打EntityManager.merge()是最佳解決方案嗎?

謝謝大家提前!

回答

3

的問題是,你違反了對象的身份。您的人員對象來自不同的事務,因此不同的持久性單元被分離。通過從你的新消息中引用它們,你已經損壞了你的持久性單元,因爲你現在如何引用從管理對象分離的對象。即如果你爲任一人做一個find(),你會得到一個不同的Person。

您應該首先在當前事務/持久化上下文中查找每個人,然後使用它們創建消息,或者在新消息上使用merge()而不是persist()作爲合併解析分離對象。

創建後消息對象不在人員消息中的原因是您正在使用共享緩存(默認爲EclipseLink),如果沒有合併,您從未將消息添加到管理人員對象(僅限於分離的)。由於ManyToOne定義了數據庫中的關係,因此您可以禁用緩存或調用人員刷新並獲取正確的消息。但是,正確的解決方案是不腐蝕你的對象模型。

見, http://en.wikibooks.org/wiki/Java_Persistence/Caching#Object_Identity

0

我做的是,當你堅持你的消息的接收者和發送者的實體不會自動保存到數據庫中。

我想你應該正確設置級聯選項。也許這篇文章可能有幫助。

http://www.mkyong.com/hibernate/cascade-jpa-hibernate-annotation-common-mistake/

+0

我不認爲它應該是必要的,因爲關係在'Message'側登記保存發送者和接收者。另外,我沒有看到任何級聯的原因(實際上可能是刪除級聯),因爲「PERSIST」級聯不起作用(「人員」已經被保留)並且「MERGE」級聯不會遵循。但是我可能會錯過一些東西......(OTOH,非常感謝這篇文章,這真的很棒)。 – brandizzi 2011-03-24 20:53:31