Close

JPA - @SequenceGenerator Examples

[Updated: May 31, 2017, Created: Mar 20, 2017]

In the last tutorial, we saw how to use 'strategy' element of the @GeneratedValue annotation. In this tutorial we are going to see the use of another element 'generator'. It is the name of the primary key generator as specified in the @SequenceGenerator or @TableGenerator annotation. These two annotations controls how database sequence or table is mapped. @SequenceGenerator and @TableGenerator can be used on the entity class or on the primary key field or property. In this tutorial we are going to focus on @SequenceGenerator only. We will explore an example of @TableGenerator in the next tutorial.

@SequenceGenerator has following elements :
'name', 'catalog', 'schema', 'sequenceName', 'initialValue' and 'allocationSize'.
These elements are self-explanatory except for 'allocationSize'.

What is allocationSize element of @SequenceGenerator

'allocationSize' doesn't mean that the entities ids will increase by this value but it is a number after which the database query will be made again to get the next database sequence value. On the application side, ids for an entities instances will always increase by 1 unless we reach the allocationSize limit. After 'allocationSize' is reached, the next id will be retrieved from the database sequence again. In case if application restarts or redeployed before allocationSize limit is reached, we will see a one-time jump in the next value. 'allocationSize' is to improve performance.

Note that if we are not generating schema via JPA then we should specify the values of @SequenceGenerator which should be consistent with existing database sequence.

All elements of @SequenceGenerator are optional except for 'name' element. The default value of 'allocationSize' is 50 and that of 'initialValue' is 1.

In following examples we are going to use Hibernate as JPA provider and H2 in-memory database.

A sequence without @SequenceGenerator example

@Entity
public class MyEntity1 {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private int myId;
}
public class SequenceGeneratorExample1 {

  public static void main(String[] args) throws Exception {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("test1");
      EntityManager em = emf.createEntityManager();

      Util.nativeQuery(em, "SELECT * FROM INFORMATION_SCHEMA.SEQUENCES");

      MyEntity1 entity1 = new MyEntity1();
      MyEntity1 entity2 = new MyEntity1();
      em.getTransaction().begin();
      em.persist(entity1);
      em.persist(entity2);
      em.getTransaction().commit();

      Util.nativeQuery(em, "SELECT * FROM MyEntity1");

      em.close();
      emf.close();
  }
}

Output

--------
SELECT * FROM INFORMATION_SCHEMA.SEQUENCES
[TEST, PUBLIC, HIBERNATE_SEQUENCE, 0, 1, false, , 32, 1, 9223372036854775807, false, 4]
--------
SELECT * FROM MyEntity1
1
2

The above output shows that by default hibernate creates a sequence with following attributes:

sequence_name = HIBERNATE_SEQUENCE
current_value = 0
increment = 1
is_generated = false
remarks =
cache = 32
min_value = 1
max_value = 9223372036854775807
is_cycle = false
id = 4

See also the useful queries of the standard database information schema here.

A sequence with @SequenceGenerator example

@Entity
public class MyEntity2 {
  @Id
  @SequenceGenerator(name = "mySeqGen", sequenceName = "mySeq", initialValue = 5, allocationSize = 100)
  @GeneratedValue(generator = "mySeqGen")
  private int myId;
}
public class SequenceGeneratorExample2 {

  public static void main(String[] args) throws Exception {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("test2");
      EntityManager em = emf.createEntityManager();

      Util.nativeQuery(em, "SELECT * FROM INFORMATION_SCHEMA.SEQUENCES");

      MyEntity2 entity1 = new MyEntity2();
      MyEntity2 entity2 = new MyEntity2();
      em.getTransaction().begin();
      em.persist(entity1);
      em.persist(entity2);
      em.getTransaction().commit();

      Util.nativeQuery(em, "SELECT * FROM MyEntity2");

      em.close();
      emf.close();
  }
}

Output

--------
SELECT * FROM INFORMATION_SCHEMA.SEQUENCES
[TEST, PUBLIC, MYSEQ, -95, 100, false, , 32, 1, 9223372036854775807, false, 4]
--------
SELECT * FROM MyEntity2
5
6

The above output shows that generated sequence has first three values consistent with our SequenceGenerator:

sequence_name = MYSEQ
current_value = -96
increment = 100

If @SequenceGenerator is not being consistent with existing database sequence

If the database sequence already exists or if we don't want it to be auto-generated then:

  • 'sequenceName' must be equal to database 'sequence_name'.
  • 'initialValue' will be ignored and database current_value will be used.
  • 'allocationSize' cannot be greater than database 'increment' otherwise we will have EntityExistsException at some point.

Check out a related example here

Same generator used by multiple entities

@Entity
public class MyEntity2 {
  @Id
  @SequenceGenerator(name = "mySeqGen", sequenceName = "mySeq", initialValue = 5, allocationSize = 100)
  @GeneratedValue(generator = "mySeqGen")
  private int myId;
}
@Entity
public class MyEntity3 {
  @Id
  @SequenceGenerator(name = "mySeqGen", sequenceName = "mySeq", initialValue = 5, allocationSize = 100)
  @GeneratedValue(generator = "mySeqGen")
  private int myId;
}
public class SequenceGeneratorExample3 {

  public static void main(String[] args) throws Exception {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("test3");
      EntityManager em = emf.createEntityManager();

      Util.nativeQuery(em, "SELECT * FROM INFORMATION_SCHEMA.SEQUENCES");

      MyEntity2 entity1 = new MyEntity2();
      MyEntity2 entity2 = new MyEntity2();
      MyEntity3 entity3 = new MyEntity3();
      MyEntity3 entity4 = new MyEntity3();
      em.getTransaction().begin();
      em.persist(entity1);
      em.persist(entity2);
      em.persist(entity3);
      em.persist(entity4);
      em.getTransaction().commit();

      Util.nativeQuery(em, "SELECT * FROM MyEntity2");
      Util.nativeQuery(em, "SELECT * FROM MyEntity3");

      em.close();
      emf.close();
  }
}

Output

--------
SELECT * FROM INFORMATION_SCHEMA.SEQUENCES
[TEST, PUBLIC, MYSEQ, -95, 100, false, , 32, 1, 9223372036854775807, false, 4]
--------
SELECT * FROM MyEntity2
5
6
--------
SELECT * FROM MyEntity3
106
107

Above output shows if two or more entities share the same generator elements, then 'allocationSize' is maintained for each separately. Note that this is Hibernate specific behavior. According to JPA specification (JSR 338, 11.1.48):

The scope of the generator name is global to the persistence unit (across all generator types).

That means the same @SequenceGenerator should share the same runtime counter of 'allocationSize'. EclipseLink follows this part of specification. Checkout a related example here.

Example Project

Dependencies and Technologies Used:

  • h2 1.4.193: H2 Database Engine.
  • hibernate-core 5.2.8.Final: The core O/RM functionality as provided by Hibernate.
    Implements javax.persistence:javax.persistence-api version 2.1
  • JDK 1.8
  • Maven 3.3.9

Sequence Genenerator Annotation Example Select All Download
  • jpa-seq-gen-annotation
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyEntity2.java
          • resources
            • META-INF

    See Also