6

MyEntity is a classic JPA entity with a generated id. I would like to automatically set a field (myStringField) to a value based on the id. In this example, I’d like to set it to “foo43” if the id is 43.

As id is auto-generated by the DB, it is null before the em.persist(myEntity) call. But after the persist, it has an id assigned by Hibernate (which called NEXTVAL on the DB sequence). So, I though of declaring a @PostPersist method, as follow:

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;
    }
    ...
}

Here is some code that creates and persists the entity:

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());
   }
}

It prints the following output:

The id = 47
foo47

which is fine. But, in the DB, the field myStringField is null ! Does anybody have an idea why?

I did expect the entity would be marked as dirty during the MyEntity.postPersist() method and that an update would occur at the end of the transaction, during the flush. But apprently not.

I use Hibernate v4.2.4

Does someone know why this DB record has a null value?

Buh Buh
  • 7,443
  • 1
  • 34
  • 61

5 Answers5

2

That's it! I made additional tests and indeed, the @PostPersist is called during the flush (probably because the SQL INSERT is sent during the flush). So, to summarize, things happen in that order: 1. em.persist(), 1.1 NEXTVAL sent to DB and myEntity.id is assigned, 2 end of @Transactional method reached and flush performed. 2.1 INSERT sent to the DB. 2.2 @PostPersist called. 2.2.1 myEntity.myStringField is assigned and entity is dirty. Then no further flush (and dirty checking and update) is performed => no DB save. Many thanks!!!

0

You are doing it POST persist, so it happens AFTER the write to the database.

According to an answer to JPA @PostPersist usage the spec states

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

Community
  • 1
  • 1
digitaljoel
  • 26,265
  • 15
  • 89
  • 115
0

The @PostPersist code is running after MyEntity is saved. So if you want to make sure the field myStringField gets saved with the updated value: "foo" + id, then after the @PostPersist code you will need to save the entity again.

So if you change your code to:

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

This, in theory should save updated myStringField correctly. This is a bit awkward though. Not sure of your entire use case but you may consider writing an 'on insert trigger' in the database itself that will take care of this for you.

lostdorje
  • 6,150
  • 9
  • 44
  • 86
0

use

@PrePersist

instead, but your method field generation is not correct for me, because if your myStringField is about to be a business id, different from the jpa id, you should assume it's creation on bean initialization.

for more example on how to deal with business id, i recommand you to see this thread about equals()/hashcode() dilemma and specially this response : https://stackoverflow.com/a/5103360/2598693

Community
  • 1
  • 1
fla
  • 143
  • 4
0

To answer your question, you shouldn't be doing that kind of stuff in you data layer. That insert and update should be handled by your business logic layer.

But use @PrePersist if you really need it in the data layer.