<<Home: Back to http://devdoc.net

Hibernate.orgCommunity Documentation

Hibernate Domain Model Mapping Guide

Hibernate - Relational Persistence for Idiomatic Java

5.1.0.Final

2016-02-10


Table of Contents

Preface
1. Data categorizations
1.1. Value types
1.2. Entity Types
1.3. Significance of type categories
2. Entity
2.1. POJO Models
2.1.1. Prefer non-final classes
2.1.2. Implement a no-argument constructor
2.1.3. Declare getters and setters for persistent attributes
2.1.4. Provide identifier attribute(s)
2.1.5. Mapping the entity
2.1.6. Mapping optimistic locking
2.1.7. Inheritance
3. Basic Types
3.1. Hibernate-provided BasicTypes
3.2. The @Basic annotation
3.3. The @Column annotation
3.4. BasicTypeRegistry
3.5. Explicit BasicTypes
3.6. Custom BasicTypes
3.7. Mapping enums
3.7.1. @Enumerated
3.7.2. AttributeConverter
3.7.3. Custom type
3.8. Mapping LOBs
3.9. Mapping Nationalized Character Data
3.10. Mapping UUID Values
3.10.1. UUID as binary
3.10.2. UUID as (var)char
3.10.3. PostgeSQL-specific UUID
3.10.4. UUID as identifier
3.11. Mapping Date/Time Values
3.12. JPA 2.1 AttributeConverters
4. Compositions
4.1. Component / Embedded
4.2. Multiple compositions
4.2.1. JPA's AttributeOverride
4.2.2. ImplicitNamingStrategy
4.3. Collections of compositions
4.4. Compositions as Map key
4.5. Compositions as identifiers
5. Collections
5.1. Collections as a value type
5.2. Collections of value types
5.3. Collections of entities
5.4. List - index
5.5. Map - key
5.6. Bags
5.7. Arrays
5.8. Collections as basic value type
6. Identifiers
6.1. Simple identifiers
6.2. Composite identifiers
6.2.1. Composite identifiers - aggregated (EmbeddedId)
6.2.2. Composite identifiers - non-aggregated (IdClass)
6.3. Generated identifier values
6.3.1. Interpreting AUTO
6.3.2. Using sequences
6.3.3. Using IDENTITY columns
6.3.4. Using identifier table
6.3.5. Using UUID generation
6.3.6. Using @GenericGenerator
6.3.7. Optimizers
6.4. Derived Identifiers
7. Natural Ids
7.1. Natural Id Mapping
7.2. Natural Id API
7.3. Natural Id - Mutability and Caching

List of Tables

3.1. Standard BasicTypes
3.2. BasicTypes added by hibernate-java8

List of Examples

1.1. Simple table and domain model
2.1. Simple @Entity
2.2. Simple @Entity with @Table
2.3. Version
3.1. With @Basic
3.2. Without @Basic
3.3. Explicit column naming
3.4. Using @org.hibernate.annotations.Type
3.5. Custom BasicType implementation
3.6. Custom UserType implementation
3.7. @Enumerated(ORDINAL) example
3.8. @Enumerated(STRING) example
3.9. Enum mapping with AttributeConverter example
3.10. Enum mapping with custom Type example
3.11. CLOB - SQL
3.12. CLOB - locator mapping
3.13. CLOB - materialized mapping
3.14. CLOB - materialized char[] mapping
3.15. BLOB - SQL
3.16. BLOB - locator mapping
3.17. BLOB - materialized mapping
3.18. NVARCHAR mapping
3.19. NCLOB (locator) mapping
3.20. NCLOB (materialized) mapping
4.1. Simple composition example
4.2. Simple Embedded
4.3. Person table
4.4. Alternative to composition
4.5. Multiple compositions
4.6. JPA's AttributeOverride
4.7. Enabling composition-safe implicit naming
4.8. Enabling composition-safe implicit naming
5.1. Delimited set of tags
6.1. Simple assigned identifier
6.2. Simple generated identifier
6.3. Basic EmbeddedId
6.4. EmbeddedId with ManyToOne
6.5. Basic IdClass
6.6. IdClass with ManyToOne
6.7. IdClass with partial generation
6.8. Unnamed sequence
6.9. Named sequence
6.10. Configured sequence
6.11. Table generator table structure
6.12. Unnamed table generator
6.13. Implicitly using the random UUID strategy
6.14. Implicitly using the random UUID strategy
7.1. Natural id using single basic attribute
7.2. Natural id using single embedded attribute
7.3. Natural id using multiple persistent attributes
7.4. Using NaturalIdLoadAccess
7.5. Using SimpleNaturalIdLoadAccess
7.6. Mutable natural id
7.7. Mutable natural id synchronization use-case
7.8. Natural id caching

The goal of this document is to discuss how to map an application's domain model to a relational database.

Historically applications using Hibernate would have used its proprietary XML mapping file format for this purpose. With the coming of JPA, most of this information is now defined in a way that is portable across ORM/JPA providers using annotations (and/or standardized XML format). This document will focus on JPA mapping where possible. For Hibernate mapping features not supported by JPA we will prefer Hibernate extension annotations, again where possible.

Before diving into the actual topics of how to map different categories of things in your domain mode it helps to understand what those categories are. Hibernate and JPA both express these categorizations.

Hibernate understands both the Java and JDBC representations of application data. The ability to read and write the this data to/from the database is the function of a Hibernate type. A type, in this usage, is an implementation of the org.hibernate.type.Type interface. This Hibernate type also describes various aspects of behavior of the Java type such as how to check for equality, how to clone values, etc.

Usage of the word type

The Hibernate type is neither a Java type nor a SQL datatype. It provides information about both of these as well as understanding marshalling between.

When you encounter the term type in discussions of Hibernate, it may refer to the Java type, the JDBC type, or the Hibernate type, depending on context.

To help understand these categorizations, lets look at a simple table and domain model that we wish to map.


In the broadest sense, Hibernate categorizes types into two groups:

Section 2.1 The Entity Class of the JPA 2.1 specification defines its requirements for an entity class. Applications that wish to remain portable across JPA providers should adhere to these requirements.

  • The entity class must be annotated with the javax.persistence.Entity annotation (or be denoted as such in XML mapping)

  • The entity class must have a public or protected no-argument constructor. It may define additional constructors as well.

  • The entity class must be a top-level class.

  • An enum or interface may not be designated as an entity.

  • The entity class must not be final. No methods or persistent instance variables of the entity class may be final.

  • If an entity instance is to be used remotely as a detached object, the entity class must implement the Serializable interface.

  • Both abstract and concrete classes can be entities. Entities may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes.

  • The persistent state of an entity is represented by instance variables, which may correspond to JavaBean-style properties. An instance variable must be directly accessed only from within the methods of the entity by the entity instance itself. The state of the entity is available to clients only through the entity’s accessor methods (getter/setter methods) or other business methods.

Hibernate, however, is not as strict in its requirements. The differences from the list above include:

  • The entity class must have a no-argument constructor, which may be public, protected or package visibility. It may define additional constructors as well.

  • The entity class need not be a top-level class.

  • Technically Hibernate can persist final classes or classes with final persistent state accessor (getter/setter) methods. However, it is generally not a good idea as doing so will stop Hibernate from being able to generate proxies for lazy-loading the entity.

  • Hibernate does not really care if you expose direct access to your instance variables and use them from outside the entity itself. The validity of such a paradigm, however, is debatable at best.

Let's look at each requirement in detail.

JPA defines support for optimistic locking based on either a version (sequential numeric) or timestamp strategy. To enable this style of optimistic locking simply add the javax.persistence.Version to the persistent attribute that defines the optimistic locking value. According to JPA, the valid types for these attributes are limited to:

  • int, or Integer

  • short, or Short

  • long, or Long

  • java.sql.Timestamp


Hibernate supports a form of optimistic locking that does not require a dedicated "version attribute". This is intended mainly for use with modeling legacy schemas. The idea is that you can get Hibernate to perform "version checks" using either all of the entity's attributes, or just the attributes that have changed. This is achieved through the use of the org.hibernate.annotations.OptimisticLocking annotation which defines a single attribute of type org.hibernate.annotations.OptimisticLockType. There are 4 available OptimisticLockTypes:

  • NONE - optimistic locking is disabled. Even if there is a @Version annotation present.

  • VERSION (the default) - performs optimistic locking based on a @Version as described above.

  • ALL - Perform optimistic locking based on *all* fields as part of an expanded WHERE clause restriction for the UPDATE/DELETE SQL statement.

  • DIRTY - Perform optimistic locking based on *dirty* fields as part of an expanded WHERE clause restriction for the UPDATE/DELETE SQL statement

* dynamic models (hbm.xml) * Map mode * proxy solutions (hibernate-core/src/test/java/org/hibernate/test/dynamicentity/tuplizer2)

Basic value types usually map a single database value, or column, to a single, non-aggregated Java type. Hibernate provides a number of built-in basic types, which follow the natural mappings recommended in the JDBC specifications.

Internally Hibernate uses a registry of basic types when it needs to resolve the specific org.hibernate.type.Type to use in certain situations.

Table 3.1. Standard BasicTypes

Hibernate type (org.hibernate.type package)JDBC typeJava typeBasicTypeRegistry key(s)
StringTypeVARCHARjava.lang.Stringstring, java.lang.String
MaterializedClobCLOBjava.lang.Stringmaterialized_clob
TextTypeLONGVARCHARjava.lang.Stringtext
CharacterTypeCHARchar, java.lang.Characterchar, java.lang.Character
BooleanTypeBITboolean, java.lang.Booleanboolean, java.lang.Boolean
NumericBooleanTypeINTEGER, 0 is false, 1 is trueboolean, java.lang.Booleannumeric_boolean
YesNoTypeCHAR, 'N'/'n' is false, 'Y'/'y' is true. The uppercase value is written to the database.boolean, java.lang.Booleanyes_no
TrueFalseTypeCHAR, 'F'/'f' is false, 'T'/'t' is true. The uppercase value is written to the database.boolean, java.lang.Booleantrue_false
ByteTypeTINYINTbyte, java.lang.Bytebyte, java.lang.Byte
ShortTypeSMALLINTshort, java.lang.Shortshort, java.lang.Short
IntegerTypesINTEGERint, java.lang.Integerint, java.lang.Integer
LongTypeBIGINTlong, java.lang.Longlong, java.lang.Long
FloatTypeFLOATfloat, java.lang.Floatfloat, java.lang.Float
DoubleTypeDOUBLEdouble, java.lang.Doubledouble, java.lang.Double
BigIntegerTypeNUMERICjava.math.BigIntegerbig_integer, java.math.BigInteger
BigDecimalTypeNUMERICjava.math.BigDecimalbig_decimal, java.math.bigDecimal
TimestampTypeTIMESTAMPjava.sql.Timestamptimestamp, java.sql.Timestamp
TimeTypeTIMEjava.sql.Timetime, java.sql.Time
DateTypeDATEjava.sql.Datedate, java.sql.Date
CalendarTypeTIMESTAMPjava.util.Calendarcalendar, java.util.Calendar
CalendarDateTypeDATEjava.util.Calendarcalendar_date
CurrencyTypejava.util.CurrencyVARCHARcurrency, java.util.Currency
LocaleTypeVARCHARjava.util.Localelocale, java.utility.locale
TimeZoneTypeVARCHAR, using the TimeZone IDjava.util.TimeZonetimezone, java.util.TimeZone
UrlTypeVARCHARjava.net.URLurl, java.net.URL
ClassTypeVARCHAR (class FQN)java.lang.Classclass, java.lang.Class
BlobTypeBLOBjava.sql.Blobblog, java.sql.Blob
ClobTypeCLOBjava.sql.Clobclob, java.sql.Clob
BinaryTypeVARBINARYbyte[]binary, byte[]
MaterializedBlobTypeBLOBbyte[]materized_blob
ImageTypeLONGVARBINARYbyte[]image
WrapperBinaryTypeVARBINARYjava.lang.Byte[]wrapper-binary, Byte[], java.lang.Byte[]
CharArrayTypeVARCHARchar[]characters, char[]
CharacterArrayTypeVARCHARjava.lang.Character[]wrapper-characters, Character[], java.lang.Character[]
UUIDBinaryTypeBINARYjava.util.UUIDuuid-binary, java.util.UUID
UUIDCharTypeCHAR, can also read VARCHARjava.util.UUIDuuid-char
PostgresUUIDTypePostgreSQL UUID, through Types#OTHER, which complies to the PostgreSQL JDBC driver definitionjava.util.UUIDpg-uuid
SerializableTypeVARBINARYimplementors of java.lang.Serializable Unlike the other value types, multiple instances of this type are registered. It is registered once under java.io.Serializable, and registered under the specific java.io.Serializable implementation class names.
StringNVarcharTypeNVARCHARjava.lang.Stringnstring
NTextTypeLONGNVARCHARjava.lang.Stringntext
NClobTypeNCLOBjava.sql.NClobnclob, java.sql.NClob
MaterializedNClobTypeNCLOBjava.lang.Stringmaterialized_nclob
PrimitiveCharacterArrayNClobTypeNCHARchar[]N/A
CharacterNCharTypeNCHARjava.lang.Characterncharacter
CharacterArrayNClobTypeNCLOBjava.lang.Character[]N/A


These mappings are managed by a service inside Hibernate called the org.hibernate.type.BasicTypeRegistry, which essentially maintains a map of org.hibernate.type.BasicType (a org.hibernate.type.Type specialization) instances keyed by a name. That is the purpose of the "BasicTypeRegistry key(s)" column in the previous tables. We will revisit this detail later.

Strictly speaking, a basic type is denoted with the javax.persistence.Basic annotation. Generally speaking the @Basic annotation can be ignored. Both of the following examples are ultimately the same.



The @Basic annotation defines 2 attributes.

  • optional - boolean (defaults to true) - Defines whether this attribute allows nulls. JPA defines this as "a hint", which essentially means that it affect is specifically required. As long as the type is not primitive, Hibernate takes this to mean that the underlying column should be NULLABLE.

  • fetch - FetchType (defaults to EAGER) - Defines whether this attribute should be fetched eagerly or lazily. JPA says that EAGER is a requirement to the provider (Hibernate) that the value should be fetched when the owner is fetched but that LAZY is merely a hint that the value be fetched when the attribute is accessed. Hibernate ignores this setting for basic types unless you are using bytecode enhancement. See the Hibernate User Guide for additional information on fetching and on bytecode enhancement.

We said before that a Hibernate type is not a Java type, nor a SQL type, but that it understands both and performs the marshalling between them. But looking at the basic type mappings from the previous examples, how did Hibernate know to use its org.hibernate.type.StringType for mapping for java.lang.String attributes or its org.hibernate.type.IntegerType for mapping java.lang.Integer attributes?

The answer lies in a service inside Hibernate called the org.hibernate.type.BasicTypeRegistry, which essentially maintains a map of org.hibernate.type.BasicType (a org.hibernate.type.Type specialization) instances keyed by a name.

We will see later (Section 3.5, “Explicit BasicTypes”) that we can explicitly tell Hibernate which BasicType to use for a particular attribute. But first let's explore how implicit resolution works and how applications can adjust implicit resolution.

Note

A thorough discussion of the BasicTypeRegistry and all the different ways to contribute types to it is beyond the scope of this documentation. Please see Integrations Guide for complete details.

As an example, take a String attribute such as we saw before with Product#sku. Since there was no explicit type mapping, Hibernate looks to the BasicTypeRegistry to find the registered mapping for java.lang.String. This goes back to the "BasicTypeRegistry key(s)" column we saw in the tables at the start of this chapter.

As a baseline within BasicTypeRegistry, Hibernate follows the recommended mappings of JDBC for Java types. JDBC recommends mapping Strings to VARCHAR, which is the exact mapping that StringType handles. So that is the baseline mapping within BasicTypeRegistry for Strings.

Applications can also extend (add new BasicType registrations) or override (replace an exiting BasicType registration) using one of the MetadataBuilder#applyBasicType methods or the MetadataBuilder#applyTypes method during bootstrap. For more details, see Section 3.6, “Custom BasicTypes”

Hibernate makes it relatively easy for developers to create their own basic type mappings type. For example, you might want to persist properties of type java.lang.BigInteger to VARCHAR columns, or support completely new types.

There are 2 approaches to developing a custom BasicType. As a means of illustrating the different approaches, lets consider a use case where we need to support a class called Fizzywig from a third party library. Lets assume that Fizzywig naturally stores as a VARCHAR.

The first approach is to directly implement the BasicType interface.


The second approach is to implement the UserType interface.


For additional information on developing and registering custom types, see the Hibernate Integration Guide.

Hibernate supports the mapping of Java enums as basic value types in a number of different ways.

You can also map enums using a Hibernate custom type mapping. Let's again revisit the Gender enum example, this time using a custom Type to store the more standardized 'M' and 'F' codes.

Example 3.10. Enum mapping with custom Type example

import org.hibernate.type.descriptor.java.CharacterTypeDescriptor;

@Entity
public class Person {
	...
	@Basic
	@Type( type = GenderType.class )
	public Gender gender;
}

public enum Gender {
	MALE( 'M' ),
	FEMALE( 'F' );

	private final char code;

	private Gender(char code) {
		this.code = code;
	}

	public char getCode() {
		return code;
	}

	public static Gender fromCode(char code) {
		if ( code == 'M' || code == 'm' ) {
			return MALE;
		}
		if ( code == 'F' || code == 'f' ) {
			return FEMALE;
		}
		throw ...
	}
}

@Converter
public class GenderType
		extends AbstractSingleColumnStandardBasicType<Gender> {

	public static final GenderType INSTANCE = new GenderType();

	private GenderType() {
		super(
				CharTypeDescriptor.INSTANCE,
				GenderJavaTypeDescriptor.INSTANCE
		);
	}

	public String getName() {
		return "gender";
	}

	@Override
	protected boolean registerUnderJavaType() {
		return true;
	}
}

public static class GenderJavaTypeDescriptor
		extends AbstractTypeDescriptor<Gender> {
	public static final GenderJavaTypeDescriptor INSTANCE = new GenderJavaTypeDescriptor();

	public String toString(Gender value) {
		return value == null ? null : value.name();
	}

	public Gender fromString(String string) {
		return string == null ? null : Gender.valueOf( string );
	}

	public <X> X unwrap(Gender value, Class<X> type, WrapperOptions options) {
		return CharacterTypeDescriptor.INSTANCE.unwrap(
				value == null ? null : value.getCode(),
				type,
				options
		);
	}

	public <X> Gender wrap(X value, WrapperOptions options) {
		return CharacterTypeDescriptor.INSTANCE.wrap( value, options );
	}
}

Again, the gender column is defined as a CHAR type and would hold:

  • NULL - null

  • 'M' - MALE

  • 'F' - FEMALE

For additional details on using custom types, see Section 3.6, “Custom BasicTypes”.

Mapping LOBs (database Large OBjects) come in 2 forms, those using the JDBC locator types and those materializing the LOB data.

The JDBC LOB locator types include:

  • java.sql.Blob

    java.sql.Clob

    java.sql.NClob

Mapping materialized forms of these LOB values would use more familiar Java types such as String, char[], byte[], etc. The trade off for "more familiar" is usually performance.

For a first look lets assume we have a CLOB column that we would like to map (NCLOB character LOB data will be covered in Section 3.9, “Mapping Nationalized Character Data”).


Let's first map this using the JDBC locator.


We could also map a materialized form.


Note

How JDBC deals with LOB data varies from driver to driver. Hibernate tries to handle all these variances for you. However some drivers do not allow Hibernate to always do that in an automatic fashion (looking directly at you PostgreSQL JDBC drivers). In such cases you may have to do some extra to get LOBs working. Such discussions are beyond the scope of this guide however.

We might even want the materialized data as a char array (for some crazy reason).


We'd map BLOB data in a similar fashion.


Let's first map this using the JDBC locator.


We could also map a materialized BLOB form.


Historically Hibernate called these components. JPA calls them embeddables. Either way the concept is the same: a composition of values. For example we might have a Name class that is a composition of first-name and last-name, or an Address class that is a composition of street, city, postal code, etc.


A composition is another form of value type. The lifecycle of a composition is defined by the thing that contains it.

A composition inherits the attribute access of its parent. For details on attribute access, see ???.

Compositions can be made up of basic values as well as associations, with the caveat that compositions which are used as collection elements cannot themselves define collections.


It is certainly more convenient to work with the compositions. However, an interesting thing happens in this particular example. By default, this mapping actually will not work as-is. The problem is in how JPA defines implicit naming rules for columns that are part of a composition, which say that all of the Address compositions would map to the same implicit column names.

This occurs any time we have multiple compositions based on the same embeddable in a given parent. We have a few options to handle this issue.

The JPA-defined way to handle this situation is through the use of its AttributeOverride annotation.


Now, essentially there are no implicit column names in the Address compositions. We have explicitly named them.

Hibernate naming strategies are covered in detail in ???. However, for the purposes of this discussion, Hibernate has the capability to interpret implicit column names in a way that is safe for use with multiple compositions.


Now the "path" to attributes are used in the implicit column naming.


You could even develop your own to do special implicit naming.

Collections of compositions are specifically value collections (as compositions are a value type). Value collections are covered in detail in Section 5.2, “Collections of value types”.

The one thing to add to the discussion of value collections in regards to compositions is that the composition cannot, in turn, define collections.

Compositions can also be used as entity identifiers. This usage is covered in detail in ???

Again, compositions used as an entity identifier cannot, in turn, define collections.

Notice how all the previous examples explicitly mark the collection attribute as either ElementCollection, OneToMany or ManyToMany. Collections not marked as such, or collections explicitly maked with @Basic are treated as JPA basic values. Meaning there value is stored into a single column in the containing table.

This is sometimes beneficial. Consider a use-case such as a VARCHAR column that represents a delimited list or set of Strings.


See the Hibernate Integrations Guide for more details on developing custom value type mappings. Without the special type mapping above the "set of tags" would have simply been marshalled using serialization.

Identifiers model the primary key of an entity. They are used to uniquely identify each specific entity.

Hibernate and JPA both make the following assumptions about the corresponding database column(s):

  • 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. This is more a general guide, than a hard-fast rule as opinions vary. JPA defines the behavior of changing the value of the identifier attribute to be undefined; Hibernate simply does not support that. In cases where the values for the PK you have chosen will be updated, Hibernate recommends mapping the mutable value as a natural id, and use a surrogate id for the PK. See Chapter 7, Natural Ids.

Note

Technically the identifier does not have to map to the column(s) physically defined as the entity table's primary key. They just need to map to column(s) that uniquely identify each row. However this documentation will continue to use the terms identifier and primary key interchangeably.

Every entity must define an identifier. For entity inheritance hierarchies, the identifier must be defined just on the entity that is the root of the hierarchy.

An identifier might be simple (single value) or composite (multiple values).

Composite identifiers correspond to one or more persistent attributes. Here are the rules governing composite identifiers, as defined by the JPA specification.

Note

The restriction that a composite identifier has to be represented by a "primary key class" is a JPA restriction. Hibernate does allow composite identifiers to be defined without a "primary key class", but use of that modeling technique is deprecated and not discussed here.

The attributes making up the composition can be either basic, composite, ManyToOne. Note especially that collections and one-to-ones are never appropriate.

Modelling a composite identifier using an IdClass differs from using an EmbeddedId in that the entity defines each individual attribute making up the composition. The IdClass simply acts as a "shadow".


Non-aggregated composite identifiers can also contain ManyToOne attributes as we saw with aggregated ones (still non-portably)


With non-aggregated composite identifiers, Hibernate also supports "partial" generation of the composite values.


Note

This feature exists because of a highly questionable interpretation of the JPA specification made by the SpecJ committee. Hibernate does not feel that JPA defines support for this, but added the feature simply to be usable in SpecJ benchmarks. Use of this feature may or may not be portable from a JPA perspective.

Hibernate supports identifier value generation across a number of different types. Remember that JPA portably defines identifier value generation just for integer types.

Identifier value generation is indicates using the javax.persistence.GeneratedValue annotation. The most important piece of information here is the specified javax.persistence.GenerationType which indicates how values will be generated.

Note

The discussions below assume that the application is using Hibernate's "new generator mappings" as indicated by the hibernate.id.new_generator_mappings setting or MetadataBuilder.enableNewIdentifierGeneratorSupport method during bootstrap. This is set to true by default, however if applications set this to false the resolutions discussed here will be very different. The rest of the discussion here assumes this setting is enabled (true).

GenerationTypes

How a persistence provider interprets the AUTO generation type is left up to the provider. Hibernate interprets it in the following order:

The fallback is to consult with the pluggable org.hibernate.boot.model.IdGeneratorStrategyInterpreter contract, which is covered in detail in the Hibernate Integrations Guide. The default behavior is to look at the java type of the identifier attribute:

For implementing database sequence-based identifier value generation Hibernate makes use of its org.hibernate.id.enhanced.SequenceStyleGenerator id generator. It is important to note that SequenceStyleGenerator is capable of working against databases that do not support sequences by switching to a table as the underlying backing. This gives Hibernate a huge degree of portability across databases while still maintaining consistent id generation behavior (versus say choosing between sequence and IDENTITY). This backing storage is completely transparent to the user.

The preferred (and portable) way to configure this generator is using the JPA-defined javax.persistence.SequenceGenerator annotation.

The simplest form is to simply request sequence generation; Hibernate will use a single, implicitly-named sequence (hibernate_sequence) for all such unnamed definitions.


Or a specifically named sequence can be requested


Use javax.persistence.SequenceGenerator to specify additional configuration.


For implementing identifier value generation based on IDENTITY columns, Hibernate makes use of its org.hibernate.id.IdentityGenerator id generator which expects the identifier to generated by INSERT into the table. IdentityGenerator understands 3 different ways that the INSERT-generated value might be retrieved:

  • If Hibernate believes the JDBC environment supports java.sql.Statement#getGeneratedKeys, then that approach will be used for extracting the IDENTITY generated keys.

  • Otherwise, if Dialect#supportsInsertSelectIdentity reports true, Hibernate will use the Dialect specific INSERT+SELECT statement syntax.

  • Otherwise, Hibernate will expect that the database supports some form of asking for the most recently inserted IDENTITY value via a separate SQL command as indicated by Dialect#getIdentitySelectString

It is important to realize that this imposes a runtime behavior where the entity row *must* be physically inserted prior to the identifier value being known. This can mess up extended persistence contexts (conversations). Because of the runtime imposition/inconsistency Hibernate suggest other forms of identifier value generation be used.

There is yet another important runtime impact of choosing IDENTITY generation: Hibernate will not be able to JDBC batching for inserts of the entities that use IDENTITY generation. The importance of this depends on the application's specific use cases. If the application is not usually creating many new instances of a given type of entity that uses IDENTITY generation, then this is not an important impact since batching would not have been helpful anyway.

Most of the Hibernate generators that separately obtain identifier values from database structures support the use of pluggable optimizers. Optimizers help manage the number of times Hibernate has to talk to the database in order to generate identifier values. For example, with no optimizer applied to a sequence-generator, everytime the application asked Hibernate to generate an identifier it would need to grab the next sequence value from the database. But if we can minimize the number of times we need to communicate with the database here, the application will be able to perform better. Which is in fact the role of these optimizers.

none

No optimization is performed. We communicate with the database each and every time an identifier value is needed from the generator.

pooled-lo

The pooled-lo optimizer works on the principle that the increment-value is encoded into the database table/sequence structure. In sequence-terms this means that the sequence is defined with a greater-that-1 increment size. For example, consider a brand new sequence defined as create sequence my_sequence start with 1 increment by 20. This sequence essentially defines a "pool" of 20 usable id values each and every time we ask it for its next-value. The pooled-lo optimizer interprets the next-value as the low end of that pool. So when we first ask it for next-value, we'd get 1. We then assume that the valid pool would be the values from 1-20 inclusive. The next call to the sequence would result in 21, which would define 21-40 as the valid range. And so on. The "lo" part of the name indicates that the value from the database table/sequence is interpreted as the pool lo(w) end.

pooled

Just like pooled-lo, except that here the value from the table/sequence is interpreted as the high end of the value pool.

hilo, legacy-hilo

Define a custom algorithm for generating pools of values based on a single value from a table or sequence. These optimizers are not recommended for use. They are maintained (and mentioned) here simply for use by legacy applications that used these strategies previously.

Applications can also implement and use their own optimizer strategies, as defined by the org.hibernate.id.enhanced.Optimizer contract.

Natural ids represent unique identifiers that naturally exist within your domain model. Even if a natural id does not make a good primary key, it still is useful to tell Hibernate about it. As we will see later, Hibernate provides a dedicated, efficient API for loading and entity by its natural-id much like it offers for loading by identifier (PK).

As stated before, Hibernate provides an API for loading entities by natural id. This is represented by the org.hibernate.NaturalIdLoadAccess contract obtained via Session#byNaturalId. If the entity does not define a natural id, an exception will be thrown there.


NaturalIdLoadAccess offers 2 distinct methods for obtaining the entity:

  • load - obtains a reference to the entity, making sure that the entity state is initialized.

  • getReference - obtains a reference to the entity. The state may or may not be initialized. If the entity is associated with the Session already, that reference (loaded or not) is returned; else if the entity supports proxy generation, an uninitialized proxy is generated and returned; otherwise the entity is loaded from the database and returned.

NaturalIdLoadAccess also allows to request locking for the load. We might use that to load an entity by natural id and at the same time apply a pessimistic lock. For additional details on locking, see the Hibernate User Guide.

We will discuss the last method available on NaturalIdLoadAccess (setSynchronizationEnabled) in Section 7.3, “Natural Id - Mutability and Caching”.

Because the Company and PostalCarrier entities define "simple" natural ids, we also allow simplified access to load them based on the natural ids.


Here we see the use of the org.hibernate.SimpleNaturalIdLoadAccess contract, obtained via Session#bySimpleNaturalId. SimpleNaturalIdLoadAccess is similar to NaturalIdLoadAccess except that it does not define the using method. Instead, because these "simple" natural ids are defined based on just one attribute we can directly pass the corresponding value of that natural id attribute directly to the load and getReference methods. If the entity does not define a natural id or if the natural id it does define is not simple, an exception will be thrown there.

A natural id may be mutable or immutable. By default @NaturalId marks an immutable natural id. An immutable natural id is expected to never change values. If the values of the natural id attribute(s) can change, @NaturalId(mutable=true) should be used instead.


Within the Session, Hibernate maintains a mapping from natural id values to pk values. If natural ids values have changed it is possible for this mapping to become out of date until a flush occurs. To work around this condition, Hibernate will attempt to discover any such pending changes and adjust for them when the load or getReference method is executed. To be clear: this is only pertinent for mutable natural ids.

This "discovery and adjustment" have a performance impact. If an application is certain that none of its mutable natural ids already associated with the Session have changed, it can disable that checking by calling setSynchronizationEnabled(false) (the default is true). This will force Hibernate to circumvent the checking of mutable natural ids.


Not only can this NaturalId-to-PK resolution be cached in the Session, but we can also have it cached in the second-level cache if second level caching is enabled.