An Introduction to Identifiers in Spring Data JPA & Hibernate
This article explains the use of Identifiers as primary keys for entities within JPA and Hibernate.
- Assigned Identifiers
- Generated Identifiers
- Generic Generated Identifiers
- Composite Identifiers
- Example Code
- Summary
This article explains the different options in creating identifiers for entities within JPA and Hibernate.
Identifiers model the primary key of an entity and are used to uniquely identify a specific entity. The primary key uniquely identifies a row within the database. In theory, the identifier does not have to match the primary key, as the identifier just need to map to column(s) that uniquely identify each row. The primary key and identifier will used interchangeably as part of this article.
Primary keys/identifiers are bounded by the following constraints:
UNIQUE
- The values must uniquely identify each row.NOT NULL
- The values cannot be null. For composite ids, no part can be null.IMMUTABLE
- The values, once inserted, can never be changed.
An identifier might be a single value (simple) or multiple values (composite). Every entity must define an identifier.
Assigned Identifiers
JPA specifies that only the following types should be used as identifier attribute types: java primitives, primitive wrapper types, java.lang.String
, java.util.Date
, java.sql.Date
, java.Mmath.BigDecimal
and java.math.BigInteger
. Any other types used as an identifier will not be portable.
The following is an example of a simple assigned identifier. The application is responsible for assigning an identifier value before invoking the save operation.
@Entity
public class MyEntity {
@Id
public Integer id;
}
The @Id Annotation specifies the primary key of an entity.
The mapped column for the primary key of the entity is assumed to be the primary key of the primary table. If no Column annotation is specified, the primary key column name is assumed to be the name of the primary key property or field.
Generated Identifiers
Hibernate provides the ability for simple identifiers to be generated. This is achieved by adding the @GeneratedValue
annotation to the identifier attribute. Hibernate will generate the identifier value once the entity is persisted.
The javax.persistence.GeneratedValue
annotation indicates the identifier value is generated. The javax.persistence.GenerationType
indicates how the value will be generated. There are four primary key generation strategy types:
AUTO
- Indicates that the persistence provider should pick an appropriate strategy for the particular database.IDENTITY
- Indicates that the persistence provider must assign primary keys for the entity using a database identity column.SEQUENCE
- Indicates that the persistence provider must assign primary keys for the entity using a database sequence.TABLE
- Indicates that the persistence provider must assign primary keys for the entity using an underlying database table to ensure uniqueness.
GenerationType.AUTO
The generation type GenerationType.IDENTITY
indicates that the persistence provider should pick an appropriate strategy for the particular database. The persistence provider decides on how it interprets the AUTO generation type. The java type of the identifier attribute provides an indication of how the provider will generate the identifier value.
If the identifier type is numerical (Long, Integer), then Hibernate is going to use the IdGeneratorStrategyInterpreter
to resolve the identifier generator strategy.
@Entity
public class City {
@Id
@GeneratedValue
private Long id;
}
If the identifier type is UUID, Hibernate is going to use an UUID identifier. This is supported through its org.hibernate.id.UUIDGenerator
id generator. UUIDGenerator supports pluggable strategies for exactly how the UUID is generated. The default strategy is a version 4, but has an alternative strategy which is a RFC 4122 version 1.
@Entity
public class Country {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
}
GenerationType.IDENTITY
The generation type GenerationType.IDENTITY
indicates that the persistence provider must assign primary keys for the entity using a database identity column. The org.hibernate.id.IdentityGenerator
id generator is used by hibernate and expects the identifier to generated by INSERT
into the table.
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
Auto-increment allows a unique number to be generated automatically when a new record is inserted into a table. By default, the starting value for AUTO_INCREMENT
is 1, and it will increment by 1 for each new record.
CREATE TABLE address (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
...
);
GenerationType.SEQUENCE
The generation type GenerationType.SEQUENCE
indicates that the persistence provider must assign primary keys for the entity using a database sequence. The org.hibernate.id.enhanced.SequenceStyleGenerator
id generator is used by hibernate.
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
The annotation type SequenceGenerator
defines a primary key generator that may be referenced by name when a generator element is specified for the GeneratedValue
annotation. The following attributes may be used:
name
- A unique generator name that can be referenced by one or more classes to be the generator for primary key values.sequenceName
- The name of the database sequence object from which to obtain primary key values.initialValue
- The value from which the sequence object is to start generating.allocationSize
- The amount to increment by when allocating sequence numbers from the sequence.
@Entity
public class Store {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="store-sequence-generator")
@SequenceGenerator(name = "store-sequence-generator", sequenceName = "user_sequence", initialValue = 5 , allocationSize=1)
private Long id;
}
GenerationType.TABLE
The generation type GenerationType.TABLE
Indicates that the persistence provider must assign primary keys for the entity using an underlying database table to ensure uniqueness.
The annotation type TableGenerator
defines a primary key generator that may be referenced by name when a generator element is specified for the GeneratedValue
annotation. The following attributes may be used:
name
- A unique generator name that can be referenced by one or more classes to be the generator for id values.table
- Name of table that stores the generated id values.pkColumnName
- Name of the primary key column in the table.valueColumnName
- Name of the column that stores the last value generated.
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "table-generator")
@TableGenerator(name = "table-generator", table = "id_gen", pkColumnName = "gen_name", valueColumnName = "gen_value")
private Long id;
Generic Generated Identifiers
The @GenericGenerator
annotation is a hibernate annotation used to denote a custom generator, which can be a class or shortcut to a generator supplied by Hibernate.
Predefined Hibernate Strategy
There are a number of generic generators supplied by Hibernate that can be used to generate identifiers. All generators implement the interface org.hibernate.id.IdentifierGenerator
. This is a very simple interface. Hibernate provides a range of built-in implementations. The fully qualified class name can be used or a shortcut name provided by Hibernate.
Fully Qualified Class Name
@Entity
public class MyEntity {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(
name = "uuid",
strategy = "org.hibernate.id.UUIDGenerator",
parameters = {
@Parameter(
name = "uuid_gen_strategy_class",
value = "org.hibernate.id.uuid.CustomVersionOneStrategy"
)
}
)
public UUID id;
Hibernate Shortcuts The shortcut names for the built-in generators are as follows:
- increment - generates identifiers of type long, short or int that are unique only when no other process is inserting data into the same table.
- identity - supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL.
- sequence - uses a sequence in DB2, PostgreSQL, Oracle, SAP DB, McKoi or a generator in Interbase.
- hilo - uses a hi/lo algorithm to efficiently generate identifiers of type long, short or int, given a table and column as a source of hi values.
- seqhilo - uses a hi/lo algorithm to efficiently generate identifiers of type long, short or int, given a named database sequence.
- uuid - Generates a 128-bit UUID based on a custom algorithm.
- uuid2 - Generates a IETF RFC 4122 compliant (variant 2) 128-bit UUID.
- guid - uses a database-generated GUID string on MS SQL Server and MySQL.
- native - selects identity, sequence or hilo depending upon the capabilities of the underlying database.
- assigned - lets the application assign an identifier to the object before save() is called.
- select - retrieves a primary key, assigned by a database trigger, by selecting the row by some unique key and retrieving the primary key value.
- foreign - uses the identifier of another associated object.
- sequence-identity - a specialized sequence generation strategy that utilizes a database sequence for the actual value generation
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(columnDefinition = "BINARY(16)")
private UUID id;
Custom Strategy
To implement a custom strategy fo generating identifiers, the @GenericGenerator
annotation is used together with the fully qualified class name of the custom strategy implementation class.
@Entity
public class Staff {
@Id
@GeneratedValue(generator = "staff_id")
@GenericGenerator(
name = "staff_id",
strategy = "com.javanibble.springdata.examples.identifier.generator.StaffIdentifierGenerator"
)
private Long id;
}
The IdentifierGenerator
interface is the general contract between a class that generates unique identifiers and the session. The implementation class of this interface provides a custom identifier generation strategy. The generate
method is used to generate a new identifier.
public class StaffIdentifierGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object obj) throws HibernateException {
Random random = new Random();
int randomId = random.nextInt(1000000);
return Long.valueOf(randomId);
}
Composite Identifiers
Composite identifiers correspond to one or more persistent attributes. The composite identifier must be represented by a primary key class. The primary key class may be defined using the @EmbeddedId
annotation or defined using the @IdClass
annotation.
Composite identifiers with @EmbeddedId
The @EmbeddedId
annotation is applied to a property of an entity class to indicate a composite primary key that is an embeddable class. This can also be applied to a mapped superclass. There must be only one @EmbeddedId
annotation and no @Id
annotation when the @EmbeddedId
annotation is used.
@Entity
public class Inventory {
@EmbeddedId
private InventoryPK id;
The embeddable class must be annotated as @Embeddable
. The @Embeddable
annotation specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity. Each of the properties of the embedded object is mapped to the database table of the entity.
@Embeddable
public class InventoryPK implements Serializable {
private Long inventory_id;
private Long secondary_id;
}
Composite identifiers with @IdClass
The @IdClass
annotation specifies a composite primary key class that is mapped to multiple fields or properties of the entity.
@Entity
@IdClass(RentalPK.class)
public class Rental {
@Id
private Long rental_id;
@Id
private Long secondary_id;
The names of the properties in the primary key class and the primary key properties of the entity must correspond. The types of the properties myst be the same.
public class RentalPK implements Serializable {
private Long rental_id;
private Long secondary_id;
}
Example Code
The source code used in this example can be found on Github.
Summary
This article explained the different options in creating identifiers for entities within JPA and Hibernate. The article explained the relation between the primary key within the database table and the identifier within JPA entity. The article also explained how identifiers can be assigned or generated. The article contained multiple examples that explained the different strategies to generate identifier values. Finally, the article also explained how to create generic generated identifiers and also composite identifiers.
Follow me on any of the different social media platforms and feel free to leave comments.