问题
I have a database in which an entity (say, User) has a list of entities (say, List). As a requirement, I must have a denormalized count of the entities on the list:
@Entity
class User {
/* ... */
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Friendship> friends;
public int friendsCount;
/* ... */
}
When I add a new element to the list, I must, transactionally, increase the corresponding counter.
I tried to do so using an EntityListener:
class MyBrokenEntityListener {
@PostPersist
public void incrementCounter(Friendship friendship) {
friendship.getUser1().incrementFriendsCount();
}
}
The entity listener is being correctly called, and while debugging I can see that it correctly modifies the entity. However, Hibernate sends no UPDATE query to the database, only the INSERT query (corresponding to the Friendship entity).
I have tried different events on the EntityListener, but it does not seem to work.
What I suppose is happening here is that the entity listener is triggered by an update to the a User. It then identifies all the dirty properties of the user, which consist of the List alone. Therefore it has nothing to do about the User, it has only to insert a Friendship. It then cascades the operation to the Friendship. The entity listener is called, it modifies the user, but at this time Hibernate has already determined that it had not to worry about the user, therefore it does not update the user.
Is this reasoning correct?
If so, how can I properly achieve this?
Thanks, Bruno
回答1:
I have tried different events on the EntityListener, but it does not seem to work.
From what I can see, the one-to-many association between User
and Friendship
is bidirectional but your mapping doesn't reflect this. So could you fix your mapping first? Something like this:
@Entity
public class User1 {
@Id @GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "user1")
private List<Friendship> friends = new ArrayList<Friendship>();
public int friendsCount;
// omitting getters, setters for brevity
public void incrementFriendsCount() {
friendsCount++;
}
public void addToFriends(Friendship friend) {
this.getFriends().add(friend);
friend.setUser1(this);
}
}
Notable changes:
- I've added a
mappedBy
on the non owning side of the association - I've added a convenient method to set both sides of the link correctly when adding a
Friendship
The entity listener is called, it modifies the user, but at this time Hibernate has already determined that it had not to worry about the user, therefore it does not update the user.
Your MyBrokenEntityListener
listener works for me, I couldn't reproduce the issue using the above fixed mapping: Hibernate does detect that User
instance is stale after the Listener invocation and it does trigger an UPDATE. The following test method (running inside a transaction) passes:
@Test
public void testPostPersistInvocationOnAddFriend() {
User1 user = new User1();
Friendship friend1 = new Friendship();
user.addToFriends(friend1);
em.persist(user);
em.flush();
assertEquals(1, user.getFriendsCount());
Friendship friend2 = new Friendship();
user.addToFriends(friend2);
em.flush();
em.clear(); // so that we reload the managed user from the db
user = em.find(User1.class, user.getId());
assertEquals(2, user.getFriendsCount());
}
来源:https://stackoverflow.com/questions/3579989/modify-entity-with-entitylistener-on-cascade