在级联上使用EntityListener修改实体

时间:2010-08-26 22:18:48

标签: hibernate orm jpa cascade entitylisteners

我有一个数据库,其中一个实体(比如用户)有一个实体列表(比如List)。作为一项要求,我必须对列表中的实体进行非规范化计数:

@Entity
class User {
    /* ... */
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Friendship> friends;

    public int friendsCount;
    /* ... */
}

当我向列表中添加一个新元素时,我必须在事务上增加相应的计数器。

我尝试使用EntityListener:

class MyBrokenEntityListener {
    @PostPersist
    public void incrementCounter(Friendship friendship) {
        friendship.getUser1().incrementFriendsCount();
    }
}

正在调用实体侦听器,并且在调试时我可以看到它正确地修改了实体。但是,Hibernate不向数据库发送UPDATE查询,只发送INSERT查询(对应于友谊实体)。

我在EntityListener上尝试了不同的事件,但它似乎不起作用。

我认为这里发生的是实体监听器是由对用户的更新触发的。然后它识别用户的所有脏属性,仅由List组成。因此它与用户无关,它只需插入友谊。然后它将操作级联到友谊。实体监听器被调用,它修改了用户,但此时Hibernate已经确定它不必担心用户,因此它不会更新用户。

这种推理是否正确?

如果是这样,我怎样才能正确实现这个目标?

谢谢, 布鲁诺

1 个答案:

答案 0 :(得分:0)

  

我在EntityListener上尝试了不同的事件,但它似乎不起作用。

从我所看到的情况来看,UserFriendship之间的一对多关联是双向的,但您的映射并未反映这一点。你可以先修复你的映射吗?像这样:

@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);
    }
}

值得注意的变化:

  • 我在协会的非拥有方添加了mappedBy
  • 添加Friendship时,我添加了一种方便的方法来正确设置链接的两边
  

调用实体监听器,它会修改用户,但此时Hibernate已经确定它不必担心用户,因此它不会更新用户。

您的MyBrokenEntityListener侦听器对我有用,我无法使用上面的固定映射重现该问题:Hibernate确实在侦听器调用后检测到User实例已失效,并且它确实触发了UPDATE。以下测试方法(在事务中运行)通过:

@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());
}