1

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

@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 查询(对应于 Friendship 实体)。

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

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

这个推理正确吗?

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

谢谢,布鲁诺

4

1 回答 1

0

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

User据我所知,和之间的一对多关联Friendship是双向的,但您的映射并未反映这一点。那么你能先修复你的映射吗?像这样的东西:

@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在侦听器调用后检测到该实例已过时并且它确实触发了更新。以下测试方法(在事务中运行)通过:

@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());
}
于 2010-08-27T05:58:40.590 回答