@PostPersist 中修改的字段未在数据库中更新

Posted

技术标签:

【中文标题】@PostPersist 中修改的字段未在数据库中更新【英文标题】:Field modified in @PostPersist is not updated in the DB 【发布时间】:2013-08-22 17:06:01 【问题描述】:

MyEntity 是具有生成 id 的经典 JPA 实体。我想根据 id 自动将一个字段(myStringField)设置为一个值。在本例中,如果 id 为 43,我想将其设置为“foo43”。

由于 id 是由 DB 自动生成的,因此在 em.persist(myEntity) 调用之前它为 null。但是在持久化之后,它有一个由 Hibernate 分配的 id(在 DB 序列上称为 NEXTVAL)。所以,我想声明一个@PostPersist 方法,如下:

package ...;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PostPersist;


@Entity
public class MyEntity 

    @Id   @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String myStringField;

    @PostPersist
    public void postPersist()
        System.out.println("The id = " + id);
        this.myStringField = "foo" + id;
    

    public String getMyStringField() 
        return myStringField;
    
    ...

下面是一些创建和持久化实体的代码:

package ...;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Service
public class MySpringBean 
   @PersistenceContext EntityManager em;

   public void createMyEntity() 
       MyEntity myEntity = new MyEntity();
       em.persist(myEntity);
       System.out.println(myEntity.getMyStringField());
   

它打印以下输出:

The id = 47
foo47

这很好。但是,在数据库中,字段 myStringField 是 null !有人知道为什么吗?

我确实希望实体在 MyEntity.postPersist() 方法期间被标记为脏,并且在事务结束时,刷新期间会发生更新。但显然不是。

我使用 Hibernate v4.2.4

有人知道为什么这个数据库记录有一个空值吗?

【问题讨论】:

【参考方案1】:

就是这样!我进行了额外的测试,实际上,在刷新期间调用了@PostPersist(可能是因为在刷新期间发送了 SQL INSERT)。因此,总而言之,事情按以下顺序发生:1. em.persist(),1.1 NEXTVAL 发送到 DB 并分配 myEntity.id,2 @Transactional 方法结束并执行刷新。 2.1 INSERT 发送到数据库。 2.2 @PostPersist 调用。 2.2.1 myEntity.myStringField 被赋值,实体脏。然后不再执行刷新(以及脏检查和更新)=> 不保存数据库。非常感谢!!!

【讨论】:

【参考方案2】:

你正在做 POST 持久化,所以它发生在写入数据库之后。

根据对JPA @PostPersist usage 的回答,规范声明

These database operations may occur directly after the persist, merge, or remove operations have been invoked or they may occur directly after a flush operation has occurred

【讨论】:

【参考方案3】:

@PostPersist 代码在 MyEntity 保存后运行。因此,如果您想确保字段 myStringField 使用更新后的值保存:“foo”+ id,那么在 @PostPersist 代码之后,您将需要再次保存实体。

因此,如果您将代码更改为:

   MyEntity myEntity = new MyEntity();
   em.persist(myEntity);
   em.persist(myEntity);

这个,理论上应该正确保存更新的 myStringField。不过这有点尴尬。不确定您的整个用例,但您可以考虑在数据库本身中编写一个“插入触发器”来为您解决这个问题。

【讨论】:

【参考方案4】:

使用

@PrePersist

相反,但是您的方法字段生成对我来说不正确,因为如果您的 myStringField 即将成为业务 id,与 jpa id 不同,您应该假设它是在 bean 初始化时创建的。

有关如何处理业务 id 的更多示例,我建议您查看有关 equals()/hashcode() 困境的帖子,特别是此回复:https://***.com/a/5103360/2598693

【讨论】:

【参考方案5】:

要回答您的问题,您不应该在数据层中做那种事情。该插入和更新应由您的业务逻辑层处理。

但是如果你真的需要在数据层使用@PrePersist。

【讨论】:

以上是关于@PostPersist 中修改的字段未在数据库中更新的主要内容,如果未能解决你的问题,请参考以下文章

JPA / @PostPersist @PostUpdate - 事务

在 postPersist 事件中插入教义

Doctrine 2 - 如何在 PostPersist 中获取最后插入的 id 的 ID?

mongoose 模式未在填充字段中注册错误

jpa @postpersist @postupdate 仅在事务提交后

Kibana 可视化未在聚合 -> 术语 -> 字段中显示创建的字段