@PostPersist中修改的字段未在DB中更新

时间:2013-08-20 15:07:21

标签: hibernate jpa

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

由于DB是由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

哪个好。但是,在DB中,字段myStringField为null!有人知道为什么吗?

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

我使用的是Hibernate v4.2.4

有人知道为什么这个DB记录有空值吗?

5 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

你这样做是持久的,所以它在写入数据库之后就会发生。

根据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

答案 2 :(得分:0)

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

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

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

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

答案 3 :(得分:0)

使用

@PrePersist

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

有关如何处理业务ID的更多示例,我建议您查看有关equals()/ hashcode()困境的此线程,特别是此响应:https://stackoverflow.com/a/5103360/2598693

答案 4 :(得分:0)

要回答您的问题,您不应该在数据层中执行此类操作。该插入和更新应由您的业务逻辑层处理。

但如果您真的需要数据层,请使用@PrePersist。