Inserting the value in the "other side" of a 1-to-n relationship in JPA (using EclipseLink)
I have an app using JSF 2, JPA 2 and EJB 3.1 running on GlassFish v3 (so it uses EclipseLink 2). This application has two JPA entities, Person
and Message
and each Message
has a reference for two Person
s, the message sender and the message receiver. The Person
class has attributes providing access to sent or received Message
s. The Message
class is something like this:
@Entity
public class Message {
@ManyToOne
@JoinColumn(name="sender")
private Person sender;
@ManyToOne
@JoinColumn(name="receiver")
private Person receiver;
// ... some stuff ...
}
and the Person
class is like the following one:
@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
instances are created through a method in a DAO class:
@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;
}
}
However, something amazing happens. When I comment out the lines marked [1]
and [2]
and call the method, the message does not appear in sender
's sent messages and receiver
's received messages lists when I use these entities elsewhere, even if they are retrieved from entity manager (e.g. through EntityManager.find()
). If I uncomment it, then the message is present in the lists as expected.
I find it intriguing because I envision two situations for the case when the lines are commented out:
- the
sender
andreceiver
entities are directly 开发者_StackOverflow社区retrieved from the database and the message should appear in their lists because I saved the message and therefore the relationship is persisted in database; or - the
sender
andreceiver
entities are retrieved from some cache, so they are already have the messages added to the list.
Apparently, it does not work this way.
I found this awesome article about cache in Hibernate. Based on it, I cogitate that the issue is that, since the cache is not of objects but of values, the EntityManager.merge()
method is required for "refresh" the entities with the cached values.
Is it so? If not, what is the reason of this behavior? Is calling EntityManager.merge()
the best solution?
Thank you all in advance!
The problem is that you are violating object identity. Your person objects are coming from a different transaction, so a different persistence unit and are detached. By referencing them from your new Message your have corrupted your persistence unit, because you now how referenced to detached objects from managed objects. i.e. if you do a find() for either person you will get a different Person back.
You should either, first find() each person in the current transaction/persistence context and then create the Message with them, or use merge() on the new Message instead of persist() as merge with resolve detached objects.
The reason the message objects are not in the person's messages after your create is that you are using a shared cache (default with EclipseLink), and without the merge, you never added the messages to the managed person objects (only the detached ones). Since the ManyToOne defines the relationship in the database, you could disable caching, or call refresh on the person and get the correct messages. But, the correct solution is to not corrupt your object model.
See, http://en.wikibooks.org/wiki/Java_Persistence/Caching#Object_Identity
What I make of it is that the receiver and sender entities aren't automatically saved to the database when you persist your message.
I think you should set up the cascading options correctly. Maybe this article may be of help.
http://www.mkyong.com/hibernate/cascade-jpa-hibernate-annotation-common-mistake/
精彩评论