java - 在JPA中插入1对n关系的 "other side"中的值(使用EclipseLink)

标签 java jpa jpa-2.0 eclipselink glassfish-3

我有一个使用 JSF 2、JPA 2 和 EJB 3.1 的应用程序,在 GlassFish v3 上运行(因此它使用 EclipseLink 2)。此应用程序有两个 JPA 实体:PersonMessage,每个 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() )。如果我取消注释,则该消息将按预期出现在列表中。

我觉得这很有趣,因为我设想了当这些行被注释掉时的两种情况:

  • senderreceiver 实体直接从数据库中检索,消息应该出现在它们的列表中,因为我保存了消息,因此关系保留在数据库中;或
  • senderreceiver 实体是从某个缓存中检索的,因此它们已经将消息添加到列表中。

显然,这样不行。

我找到了这个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/

相关文章:

java - 在 neo4j 中插入 1 MB 的图像在检索时返回不同的大小 (536 KB)

java - JPA/Hibernate 关系在一个方向上工作

java - java.lang.String 中的 NullPointerException

mysql - JPA:MySQL 说表不存在,但它实际上存在

java - 递归方法的结果

java - Spring AOP配置拦截所有异常

java - FirstGui 无法解析为类型

java - 如何在EntityListener类中使用@Autowired?

java - JPA 条件谓词条件

jpa-2.0 - 使用 JAXB 进行灵活编码