My domain model has a self-referencing bi-directional relationship with relationship management done in the entity:
@Entity
public class Users implements BaseEntity<String>, Serializable {
@Id
private String username;
@ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST})
private List<User> associatedSenders;
@ManyToMany(mappedBy = "associatedSenders")
private List<User> associatedReceivers;
//
// Associated Senders
//
public List<User> getAssociatedSenders() {
if (associatedSenders == null) {
associatedSenders = new ArrayList<User>();
}
return associatedSenders;
}
public void addAssociatedSender(User sender) {
if (associatedSenders == null) {
associatedSenders = new ArrayList<User>();
}
associatedSenders.add(checkNotNull(sender));
if (!sender.getAssociatedReceivers().contains(this)) {
sender.addAssociatedReceiver(this);
}
}
public void removeAssociatedSender(User sender) {
if (associatedSenders == null) {
associatedSenders = new ArrayList<User>();
}
associatedSenders.remove(checkNotNull(sender));
if (sender.getAssociatedReceivers().contains(this)) {
sender.removeAssociatedReceiver(this);
}
}
public void setAssociatedSenders(List<User> senders) {
checkNotNull(senders);
if (associatedSenders == null) {
associatedSenders = new ArrayList<User>();
}
// first remove all previous senders
for (Iterator<User> it = associatedSenders.iterator(); it.hasNext();) {
User sender = it.next();
it.remove();
if (sender.getAssociatedReceivers().contains(this)) {
sender.removeAssociatedReceiver(this);
}
}
// now add new senders
for (User sender : senders) {
addAssociatedSender(sender);
}
}
//
// Associated Receivers
//
public List<User> getAssociatedReceivers() {
if (associatedReceivers == null) {
associatedReceivers = new ArrayList<User>();
}
return associatedReceivers;
}
/**
* <p><b>Note:</b> this method should not be used by clients, because it
* does not manage the inverse side of the JPA relationship. Instead, use
* the appropriate method at the inverse of the relationship.
*
* @param receiver
*/
protected void addAssociatedReceiver(User receiver) {
if (associatedReceivers == null) {
associatedReceivers = new ArrayList<User>();
}
associatedReceivers.add(checkNotNull(receiver));
}
/**
* <p><b>Note:</b> this method should not be used by clients, because it
* does not manage the inverse side of the JPA relationship. Instead, use
* the appropriate method at the inverse of the relationship.
*
* @param receiver
*/
protected void removeAssociatedReceiver(User receiver) {
if (associatedReceivers == null) {
associatedReceivers = new ArrayList<User>();
}
associatedReceivers.remove(checkNotNull(receiver));
}
}
When I add new user entities to the associatedSenders
collection, everything works as expected. The table in the db gets updated correctly and the in-memory relationships are correct, as well. However, when I remove a user entity from from associatedSenders
collection (or all entities from that collection), e.g. by doing a call like this:
List<User> senders = Collections.emptyList();
user.setAssociatedSenders(senders)
the database table gets updated correctly, but the next call to em.find(User.class, username)
, where username
is the id of the user who previously was in the associatedSenders
collection, reveals that the associatedReceivers
collection (the inverse side) has not been correctly updated. That is, user
is still in that collection. Only if I refresh the entity via em.refresh()
, the collection is correctly updated. Looks like the entity manager does some caching here, but this behavior seems incorrect to me.
UPDATE Probably it's worth mentioning that I'm modifying the user entity in the frontend within a JSF managed bean, i.e. while the entity is in the detached state.