我有一个使用 JSF 2、JPA 2 和 EJB 3.1 的应用程序,在 GlassFish v3 上运行(因此它使用 EclipseLink 2)。此应用程序有两个 JPA 实体:Person
和 Message
,每个 Message
都有两个 Person
的引用,消息发送者和消息接收者。 Person
类具有提供对发送或接收的 Message
的访问的属性。 Message
类是这样的:
@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
的发送中当我在其他地方使用这些实体时,消息和接收者收到的消息列表,即使它们是从实体管理器检索的(例如通过 EntityManager.find() )。如果我取消注释,则该消息将按预期出现在列表中。
我觉得这很有趣,因为我设想了当这些行被注释掉时的两种情况:
sender
和receiver
实体直接从数据库中检索,消息应该出现在它们的列表中,因为我保存了消息,因此关系保留在数据库中;或sender
和receiver
实体是从某个缓存中检索的,因此它们已经将消息添加到列表中。
显然,这样不行。
我找到了这个awesome article关于 Hibernate 中的缓存。基于此,我认为问题在于,由于缓存不是对象而是值,因此需要 EntityManager.merge() 方法来“刷新”具有缓存值的实体。
是这样吗?如果不是,这种行为的原因是什么?调用 EntityManager.merge() 是最好的解决方案吗?
先谢谢大家了!
最佳答案
问题在于您违反了对象标识。您的人员对象来自不同的事务,因此不同的持久性单元并且是分离的。通过从新消息中引用它们,您已经损坏了持久性单元,因为您现在如何引用与托管对象分离的对象。也就是说,如果您为任何一个人执行 find(),您将得到另一个人。
您应该首先 find() 当前事务/持久性上下文中的每个人,然后与他们创建消息,或者在新消息上使用 merge() 而不是 persist() 作为与解析分离对象的合并。
创建后消息对象不在人员消息中的原因是您正在使用共享缓存(EclipseLink 默认),并且如果没有合并,您从未将消息添加到托管人员对象(仅是分离的人员对象)那些)。由于 ManyToOne 在数据库中定义了关系,因此您可以禁用缓存,或者对人员调用刷新并获取正确的消息。但是,正确的解决方案是不要破坏您的对象模型。
看, http://en.wikibooks.org/wiki/Java_Persistence/Caching#Object_Identity
关于java - 在JPA中插入1对n关系的 "other side"中的值(使用EclipseLink),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5410218/