Generate Identifiers in Hibernate

As we discussed earlier, every entity class must have an identifier attribute which corresponds to the primary key column of the table. This attribute must be annotated with @Id.

@Entity
@Table(name = "USER", schema = "demo")
public class User {
    
    @Id
    @Column(name = "USER_ID")
    private long userId;
    
    // other fields and getters and setters
}

The field to which this @Id annotation is applied should be one of the following types –

  1. Any primitive type
  2. Any primitive wrapper type
  3. String
  4. java.util.Date or java.sql.Date
  5. java.math.BigDecimal or java.math.BigInteger

If you want to automatically generate the primary key value, you have to use @GeneratedValue annotation and provide a strategy to generate the primary key. If you do not use this annotation, you have to assign the identifier value manually before you save an entity.

Generation strategies

When you use @GeneratedValue annotation, you should provide a strategy to generate the identity value. We can choose one of the four strategies available in GenerationType enum.

package javax.persistence;

public enum GenerationType {
    AUTO,
    IDENTITY,
    SEQUENCE,
    TABLE
}
GenerationType.AUTO

If you use this GenerationType, hibernate will choose a GenerationType based on the SQL dialect of your database. For most popular databases, it will select GenerationType.SEQUENCE.

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long userId;

This is the default generation type. So the above code is equivalent to –

@Id
@GeneratedValue()
private long userId;

Almost all the time, you need more control on how the identity value will be generated and for that you should not rely on this generation type. You should explicitly specify the generation type that you want to use.

GenerationType.IDENTITY

The GenerationType.IDENTITY relies on a special auto-incremented database IDENTITY column and allows the database to automatically generate a new value with each insert operation. Identity columns are supported in many databases like MySQL, SQL Server etc.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long userId;
GenerationType.SEQUENCE

If you use this generation type, Hibernate will expect a sequence named hibernate_sequence in the database. Before every insert, Hibernate will fire a select statement to get the next value from the sequence and use that as the identity value.

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long userId;

Most of the time, you want to use your own sequence rather than the default sequence. To specify that sequence, You can use @SequenceGenerator.

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userId_generator")
@SequenceGenerator(name = "userId_generator", sequenceName = "userId_seq", allocationSize = 20, schema = "demo")
private long userId;

The name attribute gives the sequence generator a unique name which is used by the @GeneratedValue annotation. The sequenceName is the actual database sequence name. The allocationSize is an interesting one. It should match with database sequence increment by value. As we have used allocationSize as 20, sequence should look similar to –

CREATE SEQUENCE DEMO.USERID_SEQ INCREMENT BY 20;

The allocationSize doesn’t mean that the id value will be increased by this value. It specifies the count after which the database query will again be made to get the next database sequence value. Till the application reaches that count, on the application side, id value will be increased by 1. After allocationSize is reached, the next id will be retrieved from the database sequence again. If the application restarts before allocationSize limit is reached, the next value will be retrieved from the database sequence again. The allocationSize is used to minimize round trips to the database server.

GenerationType.TABLE

In this case, Hibernate will use an additional table to hold the next primary key value. Individual rows will be used for individual entities.

@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private long userId;

If you try to insert an entity, the next primary key will be fetched from that additional table and will be used for that insert. Then that value will be incremented by 1 in the table for next use.

The GenerationType.TABLE is rarely used nowadays and performance wise it is slower.

Composite primary key

A primary key uniquely identifies each record in a database table. We annotate the corresponding attribute with @Id annotation. What about a composite key where a combination of two or more columns form the primary key for a table?

In JPA, there are two options –

  1. Using @IdClass annotation
  2. Using @EmbeddedId annotation.

Let’s assume we have a table called Song and two columns – title and singer is used as the composite key.

In both the approaches, you have to create a separate class that will contain those composite key attributes (title & singer). That class must follow below rules –

  1. This class must be Serializable.
  2. This class must have a no-arg constructor.
  3. This class must define the equals() and hashCode() methods.
  4. This class must be public.
Using @IdClass

First create a SongId class that will contain title & singer attributes.

public class SongId implements Serializable {
    private String title;
    private String singer;
    
    // getters and setters
    // equals and hashCode
}

Then create a Song class that will contain all attributes including composite key attributes with the same name. Annotate composite key attributes with @Id in Song class. Also mark the Song class with @IdClass and pass SongId class as value.

@Entity
@Table(name = "SONG", schema = "demo")
@IdClass(SongId.class)
public class Song {
    
    @Id
    @Column(name = "TITLE")
    private String title;
    
    @Id
    @Column(name = "SINGER")
    private String singer;
    
    @Column(name = "RATING")
    private int rating;

    // other fields, getters and setters
}
Using @EmbeddedId

This is an alternative to the @IdClass annotation and personally I like this approach. In this approach, SongId must be annotated with @Embeddable.

@Embeddable
public class SongId implements Serializable {
    
    @Column(name = "TITLE")
    private String title;
    
    @Column(name = "SINGER")
    private String singer;
    
    // getters and setters
    // equals and hashCode
}

Then embed this class in the Song entity using @EmbeddedId. No need to include composite key attributes in Song class.

@Entity
@Table(name = "SONG", schema = "demo")
public class Song {
    
    @EmbeddedId
    private SongId songId;
	
    @Column(name = "RATING")
    private int rating;

    // other fields, getters and setters
}

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.