@PreUpdate与Spring Data JPA不兼容

14

我有一个实体:

@Entity
@EntityListeners(MyEntityListener.class)
class MyEntity{ ... }

而且听众:

class MyEntityListener{
    @PrePersist
    @PreUpdate
    public void doSomething(Object entity){ ... }
}

我正在使用Spring Data生成的DAO来操作这个实体(版本号为1.4.1),并且使用EclipseLink。代码行为如下:
MyEntity entity = new Entity();
entity = dao.save(entity); // the doSomething() is called here
// change something it the entity and save it again
dao.save(entity); // the doSomething() is NOT called here, checked with breakpoint

这个问题已经在2009年被某人描述过了, 但是他们没有提出任何解决方案。我想知道是否有人有解决办法?


你确定 doSomething() 第二次没有被调用吗?它可能在事务提交之前被调用,而不是立即执行。 - axtavt
你尝试过在第二次save()之前分离实体吗? - V G
@AndreiI 你说得对,当我通过 entity = dao.findOne(entity.getId()) 获取实体并在第二次保存之前,监听器方法成功地被调用。请将其作为答案发布,并说明原因,如果您知道发生了什么。 - fracz
我正在使用 JpaRepository,调用 repository.saveAndFlush(entity) 触发了 @PreUpdate 回调。希望这能帮到你。 - maximus
2个回答

11
正如你所说,如果实体被分离或再次从数据库中获取,则回调方法将第二次被调用。
我不能确切地解释它,但可以考虑描述在这里的场景,当第二个save()调用之前没有识别到脏字段,因此@PreUpdate回调未被调用。 或者它可能只是您的EclipseLink版本中的错误。
在JPA 2.0规范中,我发现以下行为(3.5.2Entities的生命周期回调方法的语义),这正是您的行为:
注意,在单个事务中持久化并随后修改实体或在单个事务中修改并随后删除实体时,PreUpdate和PostUpdate回调是否发生取决于实现。可移植的应用程序不应依赖这种行为。

2

你在两个不同的save()操作中的事务设置是什么?

我认为对于实体的不同状态(瞬态、持久态、游离态),save()/update()/merge()/persist()之间会有一些区别,操作并不像你想象的那样相同,并且你的注释@PrePersist和@PreUpdate没有生效。


我已经开启了Spring的事务管理,但是刷新似乎不是问题,因为当我将两个“save”调用替换为“saveAndFlush”时没有任何区别。 - fracz
在第一次保存后,实体仍处于持久状态。也许saveOrUpdate()可以解决你的问题。 - Terry Zhao
1
saveOrUpdate() 方法既不在 JpaRepository 接口中,也不在 CrudRepository 接口中。 - fracz
有没有一种方法可以在进行第二次保存之前分离持久实体? - Terry Zhao
我不确定这个链接是否适用于你的情况 https://dev59.com/3mYr5IYBdhLWcg3wi6zd 是否有EntityManager.detach()方法? - Terry Zhao
在第二次保存之前再次从数据库中提取实体即可解决该问题。请参见我对Andrei的上面的评论。 - fracz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接