Richard Barker Richard Barker - 2 months ago 33
Java Question

Persisting an entity when ID is null and automatically generated in the database

I'm using EclipseLink with JavaDB and every time I try to persist a new user entity I get an error relating to the fact the ID field of the new user entity is null. I know it's null -- and it should be because the ID field is generated as the Identity in the database. Or am I wrong? I've tried adding @GeneratedValue to the entity but that causes errors regardless of the generation strategy used. I've also looked here, here, and here but these questions don't seem to relate fully to my issue. The exceptions below always occur on at the call to EntityManager.persist(user).

I also tried removing @NotNull and @GeneratedValue and, oddly enough, it got past the persist call -- but only to fail on the commit. As far as I can tell I've got everything set right. So the question is: why is this application failing to add a new user and what do I need to do to fix it?

User entity:

@Entity(name = "User")
@Table(name = "USERS")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "IDENTIFIER", nullable = false,unique = true)
private Long identifier;
// class shortened for brevity
}


Corresponding to this SQL Script:

create table "USERSDBADMIN".USERS
(
IDENTIFIER BIGINT not null primary key GENERATED ALWAYS AS IDENTITY
(START WITH 0, INCREMENT BY 1),
USERNAME VARCHAR(50) not null,
EMAIL VARCHAR(150) not null,
LASTLOGIN DATE default NULL
);


User Create Method (from the JPA controller):

public void create(User user) throws PreexistingEntityException, RollbackFailureException, Exception {
EntityManager em = null;
try {
utx.begin();
em = getEntityManager();
em.persist(user);
utx.commit();
} catch (Exception ex) {
try {
utx.rollback();
} catch (Exception re) {
throw new RollbackFailureException("An error occurred attempting to roll back the transaction.", re);
}
if (findUser(user.getIdentifier()) != null) {
throw new PreexistingEntityException("User " + user + " already exists.", ex);
}
throw ex;
} finally {
if (em != null) {
em.close();
}
}
}


GenerationStrategy.Identity error (basically saying the ID field is null):

ex = (javax.validation.ConstraintViolationException) javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.


GenerationStrategy.Sequence error (Table and Auto produce same exception):

ex = (org.eclipse.persistence.exceptions.DatabaseException) Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException. Internal Exception: java.sql.SQLSyntaxErrorException: SEQUENCE 'SEQ_GEN_SEQUENCE' does not exist. Call: VALUES(NEXT VALUE FOR SEQ_GEN_SEQUENCE) Query: ValueReadQuery(sql="VALUES(NEXT VALUE FOR SEQ_GEN_SEQUENCE)")

Answer

Do not use @NotNull constraint on @Id attribute generated using GenerationType.IDENTITY strategy. The problem is @NotNull and other JSR-303 constraints are validated before commit. And the autoincremented identifier isn't available until after the commit completes.

Try this and don't care about ID initialization any more:

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @Column(name = "IDENTIFIER", nullable = false, unique = true)
 private Long identifier;