Когда JPA устанавливает @GeneratedValue @Id

У меня есть простой объект JPA, который использует сгенерированный long "ID" в качестве своего первичного ключа:

@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
}

В какой-то момент жизненного цикла объекта этого типа JPA должен вызвать setId() для записи генерируемого значения идентификатора. Мой вопрос в том, когда это произойдет, и , где находится документация, в которой указано это. Я просмотрел спецификацию JPA и не могу найти четкое выражение.

Спецификация JPA говорит (выделено мной):

Экземпляр управляемого объекта - это экземпляр с постоянным идентификатором, который в настоящее время связан с контекстом персистентности.

Является ли это попыткой сказать, что объект должен иметь значительную значимость @Id? В документации для EntityManager.persist() говорится (добавлено выделение), что делает "экземпляр управляемым и постоянным", значит ли это, что @Id установлен этим методом? Или это не до тех пор, пока вы не назовете EntityTransaction.commit()?

Когда параметр @Id установлен, может быть разным для разных поставщиков JPA и, возможно, для разных стратегий генерации. Но что является самым безопасным (переносимым, соответствующим спецификации) предположением, которое вы можете сделать о самой ранней точке жизненного цикла, которую он установил?

Ответ 1

вызов .perist() не будет автоматически устанавливать значение id. Ваш поставщик JPA гарантирует, что он будет установлен до того, как сущность будет окончательно записана в db. Поэтому вы правы предположить, что идентификатор будет назначен при совершении транзакции. Но это не единственный возможный случай. Когда вы вызовете .flush(), произойдет то же самое.

Томас

Обновление: обратите внимание на комментарий Geek, пожалуйста. → Если используется GenerationType.Identity, идентификатор не будет установлен поставщиком, прежде чем сущность будет записана на db. В этом случае генерация id происходит во время процесса вставки на уровне db. Во всяком случае, поставщик JPA будет гарантировать, что сущность будет обновляться впоследствии, и сгенерированный идентификатор будет доступен в аннотированном свойстве @Id.

Ответ 2

AFAIK, идентификатор гарантированно будет присваиваться только тогда, когда контекст сохранения сохраняется. Его можно назначить раньше, но это зависит от стратегии генерации.

Ответ 3

В книге Enterprise JavaBeans 3.1 от Rubinger и Burke говорится следующее: на стр. 143 (выделено мной):

Java Persistence также может быть настроена на автоматическое создание первичного ключа , когда метод persist() вызывается с помощью аннотации @GeneratedValue поверх поля первичного ключа или сеттера. Таким образом, в предыдущем примере, если бы мы включили автоматическое создание ключей, мы могли бы просмотреть сгенерированный ключ после завершения метода persist().

Спецификация JPA говорит (выделено мной):

Экземпляр управляемого объекта - это экземпляр с постоянным идентификатором, который в настоящее время связан с контекстом персистентности.

А также, что EntityManager.persist() делает

управляемый экземпляр и постоянный

Поскольку @Id имеет решающее значение для идентичности объекта, единственный способ для EntityManager.persist() сделать управляемый объект - установить его идентификатор, создав @Id.


Однако

Четкое утверждение Rubinger и Buke несовместимо с поведением Hibernate. Таким образом, кажется, что знающие люди не согласны с тем, что намерена спецификация JPA.

Ответ 4

В соответствии с JSR 338: Настойчивость JavaTM 2.1/3.5.3 Семантика методов обратного вызова жизненного цикла для объектов,

Методы обратного вызова PostPersist и PostRemove вызываются для объекта после того, как объект был сделан постоянным или удаленным. Эти обратные вызовы также будут вызываться для всех объектов, к которым эти операции каскадируются. Методы PostPersist и PostRemove будут вызываться после операций вставки и удаления базы данных соответственно. Эти операции с базой данных могут возникать непосредственно после того, как были вызваны операции persist, merge или remove или они могут возникнуть непосредственно после выполнения операции flush (которая может быть в конце транзакции). Сгенерированные значения первичного ключа доступны в PostPersist.

Одним из возможных (лично предполагаемых) исключений является GeneratorType.TABLE, который контейнер (может) извлекает значения для использования и (может) устанавливать его раньше PrePersist. Я всегда использую id в PrePersist. Я не уверен, что это поведение указано или может не работать с другими поставщиками.

важное изменение

Не все серверы приложений устанавливают идентификатор до PrePersist. Вы можете отслеживать JPA_SPEC.