Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manually specify the value of a primary key in JPA @GeneratedValue column

I'm having an Entity which has a primary key / id field like the following:

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

This works well. I'm using EclipseLink to create the DDL-Schema, and the column is correctly created like so:

`id` bigint(20) NOT NULL AUTO_INCREMENT 

However, I've got several entities for which I do want to specify the PK myself (it's a little application that transfers data from an old database to the new one we're building). If I specify the ID for the POJO (using setId(Long id)) and persist it, EclipseLink does not save it (i.e. the record is saved, but the id is auto generated by eclipseLink).

Is there a way to manually specify the value of a column which has a @GeneratedValue ?

Here some thoughts on the issue:

I tried to work around the problem by not using @GeneratedValue at all, but simply manually define the column to be AUTO_INCREMENTed. However this forces me to manually provide an IDs always, since EclipseLink validates the primary key (so it may not be null, zero, or a negative number). The exception message reads that I should specify eclipselink.id_validation, however this does not seem to make any difference (I annotated @PrimaryKey(validation = IdValidation.NONE) but still got the same message).

To clarify: I'm using EclipseLink (2.4.0) as persistence provider and I can't switch away from it (large portions of the project depend on eclipselink specific query hints, annotations, and classes).


EDIT (In Response to the answers):

Custom Sequencing: I tried to implement my own sequencing. I tried subclassing DefaultSequence, but EclipseLink will tell me Internal Exception: org.eclipse.persistence.platform.database.MySQLPlatform could not be found. But I've checked: The class is on the classpath.

So I subclassed another class, NativeSequence:

public class MyNativeSequence extends NativeSequence {      public MyNativeSequence() {         super();     }      public MyNativeSequence(final String name) {         super(name);     }       @Override     public boolean shouldAlwaysOverrideExistingValue() {         return false;     }      @Override     public boolean shouldAlwaysOverrideExistingValue(final String seqName) {         return false;     }  } 

However, what I get is the following:

javax.persistence.RollbackException: Exception [EclipseLink-7197] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException Exception Description: Null or zero primary key encountered in unit of work clone [de.dfv.datenbank.domain.Mitarbeiter[ id=null ]], primary key [null]. Set descriptors IdValidation or the "eclipselink.id-validation" property.     at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)     ... Caused by: Exception [EclipseLink-7197] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException Exception Description: Null or zero primary key encountered in unit of work clone [de.dfv.datenbank.domain.Mitarbeiter[ id=null ]], primary key [null]. Set descriptors IdValidation or the "eclipselink.id-validation" property.     at org.eclipse.persistence.exceptions.ValidationException.nullPrimaryKeyInUnitOfWorkClone(ValidationException.java:1451)     ... 

(stack trace shortened for clarity). This is the same message which I got before. Shouldn't I subclass NativeSequence? If so, I don't know what to implement for the abstract methods in Sequence or StandardSequence.

It may also be worth noting, that simply subclassing (without overriding any methods) the class works as expected. However, returing false in shouldAlwaysOverrideExistingValue(...) will not generate a single value at all (I stepped through the program and getGeneratedValue() is not called once).

Also, when I insert like 8 entities of a certain kind within a transaction it resulted in 11 records in the database (what the hell?!).

EDIT (2012-09-01): I still do not have a Solution for the problem, Implementing my own sequence did not solve it. What I need is a way to be able to not set an Id explicitly (so it will be auto generated) and to be able to set an Id explicitly (so it will be used for the creation of the record in the database).

I tried to define the column as auto_increment myself and ommit @GeneratedValue, however Validation will kick in and not allow me to save such an entity. If I specify a value != 0 and != zero, mysql will complain for a duplicate primary key.

I'm running out of ideas and options to try. Any? (starting a bounty)

like image 764
scravy Avatar asked Aug 17 '12 08:08

scravy


People also ask

Which JPA annotation is referred to generate ordered values for primary keys?

@GeneratedValue These are the 2 most common database features to generate unique primary key values. If you annotate your primary key attribute with the @GeneratedValue annotation, you can use a database sequence by setting the strategy attribute to GenerationType.

Which annotation is used to set the primary key for a table?

The @Id annotation offer the simplest mechanism to define the mapping to the primary key. The @IDClass annotation is used to model a composite key. Use the @EmbeddedId annotation with the @Embeddable annotation to move the definition of a composite key inside the entity.


2 Answers

This works with eclipselink. It will create a seperate table for the sequence, but that shouldn't pose a problem.

@Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(name="id", insertable=true, updatable=true, unique=true, nullable=false) private Long id; 

GenerationType.AUTO will choose the ideal generation strategy. Since the field is specified as insertable and updateable, a TABLE generation strategy will be used. This means eclipselink will generate another table holding the current sequence value and generate the sequence itself instead of delegating it to the database. Since the column is declared insertable, if id is null when persisting, eclipselink will generate the id. Otherwise the existing id will be used.

like image 122
poison Avatar answered Sep 18 '22 08:09

poison


If you use TABLE sequencing, then EclipseLink will allow you to override the value (or SEQUENCE if your database supports this, MySQL does not).

For IDENTITY, I'm not even sure that MySQL will allow you to supply your own Id, you might want to verify this. In general I would never recommend using IDENTITY as it does not support preallocation.

There are a few issues with allowing IDENTITY to provide the id or not. One is that two different insert SQL will need to be generated depending on the id value, as for IDENTITY the id cannot be in the insert at all. You may wish to log a bug to have IDENTITY support user provided ids.

You should still be able to get it working with your own Sequence subclass, or possibly MySQLPlatform subclass. You would set your MySQLPlatform subclass using the "eclipselink.target-database" persistence unit property.

like image 42
James Avatar answered Sep 17 '22 08:09

James