Search code examples
jpaeclipselinksequence

Assigning Sequences for all JPA entities using SessionCustomizer


I am trying to use a SessionCustomizer to automatically generate Sequences in EclipseLink which already exist in the database following a special naming convention. For example an entity called Item is mapped to a table called ITEMS which has a four letter alias ITEM and a database sequence called ITEM_ID_SEQ for unique ID generation.

I am using an annotation as a marker to hold the alias name on the entity class because we are using it for other purposes, too:

package jpa.namingsupport;

// imports omitted

@Target(TYPE)
@Retention(RUNTIME)
public @interface Alias {
    String name();
}

Entities look like this:

package jpa.entities;

// imports omitted

@Entity
@Table(name = "ITEMS")
@Alias(name = "ITEM")
public class Item {

    @Id
    private Long id;

    @Version
    private Long version;

    private String name;

    // setters and getters omitted
}

Using a SessionCustomizer registered correctly and verified running on startup to create and add the Sequences to the entities:

package jpa.namingsupport;

// imports omitted

public class AliasCustomizer implements SessionCustomizer {

    @Override
    public void customize(Session session) throws Exception {
        Map<Class, ClassDescriptor> entities = session.getDescriptors();
        for (Class entity : entities.keySet()) {
            customizeSequence(aliasNameFor(entity), entities.get(entity), session);
        }
    }

    private String aliasNameFor(Class entity) {
        Alias alias = (Alias) entity.getAnnotation(Alias.class);
        return alias.name();
    }

    private void customizeSequence(String alias, ClassDescriptor descriptor, Session session) {
        NativeSequence sequence = new NativeSequence(underscores(alias, "ID", "SEQ"), 1);
        session.getLogin().addSequence(sequence);
        descriptor.setSequenceNumberName(sequence.getName());
        descriptor.setSequenceNumberField(descriptor.getPrimaryKeyFields().get(0));
        descriptor.setSequence(sequence);
    }

    private String underscores(String... parts) {
        return StringUtils.arrayToDelimitedString(parts, "_");
    }

}

But when I am running my tests the ID is not assigned from the Sequence before saving:

[EL Warning]: 2013-07-14 20:32:32.571--UnitOfWork(1908148255)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLException: NULL nicht zulässig für Feld "ITEM_ID"
NULL not allowed for column "ITEM_ID"; SQL statement:
INSERT INTO ITEMS (ITEM_NAME, ITEM_VERSION) VALUES (?, ?) [23502-172]

Any hints and ideas what I am missing in my code? What I am seeing is that there is no reference to the ITEM_ID column in the generated insert statement.


Solution

  • Why don't you just put @GeneratedValue(strategy=SEQUENCE, generator="ITME_ID_SEQ") on your id?

    For your customizer, don't call descriptor.setSequence(), this should be done be initializaiton.

    The SQL is expecting the id to being using an IDENTITY value, you need to configure your table for this. If you want to use SEQUENCE instead, then pass false into new NativeSequence(name, increment, false). H2 supports both IDENTITY and SEQUENCE, and NativeSequence defaults to using IDENTITY, false means SEQUENCE.