When does the JPA set a @GeneratedValue @Id
Asked Answered
W

4

50

I have a simple JPA entity that uses a generated long "ID" as its primary key:

@Entity
public class Player {
   private long id;

   protected Player() {
     // Do nothing; id defaults to 0L
   }


   @GeneratedValue
   @Id
   public long getId() {
      return id;
   }

   protected void setId(final long id) {
      this.id = id;
   }
   // Other code
}

At some point in the life-cycle of an object of this type the JPA must call setId() to record the generated ID value. My question is, when does this happen, and where is the documentation that states this. I've looked through the JPA Specification and can not find a clear statement.

The JPA Specification says (emphasis added):

A managed entity instance is an instance with a persistent identity that is currently associated with a persistence context.

Is that trying to say that the the object must be managed to have its @Id significant? The documentation for EntityManager.persist() says (emphasis added) it makes "an instance managed and persistent", so does that mean that the @Id is set by that method? Or is it not until you call EntityTransaction.commit()?

When the @Id is set might be different for different JPA providers, and perhaps for different generation strategies. But what is the safest (portable, specification conforming) assumption that you can make about the earliest point in the lifecycle that it has been set?

Whin answered 31/1, 2012 at 22:18 Comment(4)
Sounds like something you could establish easily enough with debugging.Borras
I'd bet that if spec doesn't explicitly say when the @Id should be generated than it's left for the vendors to decide.Assisi
@Raedwald: Debugging will tell you how JPA works internally, and tell you which bits are dialect-specific.Borras
Related, but not duplicate, question: #8170140Whin
M
25

calling .persist() will not automatically set the id value. Your JPA provider will ensure that it is set before the entity is finally written to db. So you are right to assume that the id will be assigned when the transaction is committed. But this is not the only possible case. When you call .flush() the very same will happen.

Thomas

Update: Pay attention to Geek's comment, please. -> If GenerationType.Identity is used, the id will not be set by the provider before the entity is written to db. In this case id generation happens during the insert process on db level. Anyway, the JPA provider will ensure that the entity is updated afterwards and the generated id will be available in the @Id annotated property.

Monometallic answered 31/1, 2012 at 22:43 Comment(6)
Sounds reasonable. So, if you call EntityManager.flush() can you rely on the @Generated @Id having been set? I can find no clues in the documentation.Whin
You might also have a look at this post in the hibernate forum: forum.hibernate.org/viewtopic.php?p=2384011#p2384011 It seems to depend on choosen generator strategyMonometallic
According to the EntityManager documentation docs.oracle.com/javaee/6/api/javax/persistence/… flush will "Synchronize the persistence context to the underlying database." This means that all pooled insert statements will be written to db to synchronize state. To do so your JPA provider needs those id values. So they should be available after calling flush. Some strategies might set them earlier though.Monometallic
I think you've found it, but I wish the documentation were clearer. The corresponding part of the specification is "3.2.4 Synchronization to the Database: The state of persistent entities is synchronized to the database at transaction commit. This synchronization involving writing to the database any updates to persistent entities... [including] assignment of a new value to a persistent property or field... The flush method can be used by the application to force synchronization."Whin
@Whin you could suggest some changes or clarifications to the JPA 2.1 Expert Group - i.e. through their mailing list.Assisi
@Monometallic You wrote "Your JPA provider will ensure that it is set before the entity is finally written to db". This is not entirely correct because if you are using strategy=GenerationType.IDENTITY, then it is actually only available after the entity is written to the db not before it is written to the db.Gaitan
F
12

AFAIK, the ID is only guaranteed to be assigned when the persistence context is flushed. It might be assigned sooner, but it depends on the generation strategy.

Flashcube answered 31/1, 2012 at 22:47 Comment(0)
W
11

The book Enterprise JavaBeans 3.1 by Rubinger and Burke says the following, on page 143 (emphasis added):

Java Persistence can also be configured to automatically generate a primary key when the persist() method is invoked through the use of the @GeneratedValue annotation atop the primary key field or setter. So, in the previous example, if we had auto key generation enabled, we could view the generated key after the persist() method completed.

The JPA Specification says (emphasis added):

A managed entity instance is an instance with a persistent identity that is currently associated with a persistence context.

And also that EntityManager.persist() makes

an instance managed and persistent

As the @Id is crucial to the identity of an entity, the only way for EntityManager.persist() to make the object managed is to establish its identity by generating the @Id.


However

Rubinger and Buke's clear statement is inconsistent with the behaviour of Hibernate. So it seems that knowledgeable people disgree about what the JPA specification intends.

Whin answered 26/2, 2012 at 13:24 Comment(0)
T
4

According to JSR 338: JavaTM Persistence 2.1 / 3.5.3 Semantics of the Life Cycle Callback Methods for Entities,

The PostPersist and PostRemove callback methods are invoked for an entity after the entity has been made persistent or removed. These callbacks will also be invoked on all entities to which these operations are cascaded. The PostPersist and PostRemove methods will be invoked after the database insert and delete operations respectively. 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 (which may be at the end of the transaction). Generated primary key values are available in the PostPersist method.

One possible (personally presumable) exception is GeneratorType.TABLE which the container (may) fetches values to use and (may) set it before PrePersist. I always use my id in PrePersist. I'm not sure this behaviors is specified or may not work with any other vendors.

important edit

Not all application servers set id before PrePersist. You can track JPA_SPEC.

Talebearer answered 27/7, 2016 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.