Modify entity with EntityListener on cascade

大兔子大兔子 提交于 2020-01-05 05:18:51

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!