我有一个数据库,其中一个实体(比如用户)有一个实体列表(比如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已经确定它不必担心用户,因此它不会更新用户。
这种推理是否正确?
如果是这样,我怎样才能正确实现这个目标?
谢谢, 布鲁诺
答案 0 :(得分: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
实例已失效,并且它确实触发了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());
}