Package org.hibernate.annotations

A set of mapping annotations which extend the O/R mapping annotations defined by JPA.

The JPA specification perfectly nails many aspects of the O/R persistence problem, but here we address some areas where it falls short.

Basic types in JPA

A basic type handles the persistence of an attribute of an entity or embeddable object that is stored in exactly one database column.

JPA supports a very limited set of built-in basic types.

CategoryPackageTypes
Primitive types boolean, int, double, etc.
Primitive wrappers java.lang Boolean, Integer, Double, etc.
Stringsjava.lang String
Arbitrary-precision numeric types java.mathBigInteger, BigDecimal
Date/time typesjava.time LocalDate, LocalTime, LocalDateTime, OffsetDateTime, Instant
Deprecated date/time types java.util Date, Calendar
Deprecated JDBC date/time types java.sql Date, Time, Timestamp
Binary and character arrays byte[], char[]
UUIDsjava.util UUID
Enumerated types Any enum
Serializable types Any java.io.Serializable

JPA does provide converters as an extensibility mechanism, but its converters are only useful for classes which have an equivalent representation as one of the types listed above.

Basic value type mappings

By contrast, Hibernate has an embarrassingly rich set of abstractions for modelling basic types, which can be initially confusing.

Note that the venerable interface Type abstracts over all sorts of field and property types, not only basic types. In modern Hibernate, programs should avoid direct use of this interface.

Instead, a program should use either a "compositional" basic type, or in more extreme cases, a UserType.

These two approaches cannot be used together. A UserType always takes precedence over the compositional approach.

All the typing annotations just mentioned may be used as meta-annotations. That is, it's possible to define a new typing annotation like this:

 @JavaType(ThingJavaType.class)
 @JdbcTypeCode(JSON)
 @Target({METHOD, FIELD})
 @Retention(RUNTIME)
 public @interface JsonThing {}
 
The annotation may then be applied to fields and properties of entities and embeddable objects:
 @JsonThing Thing myThing;
 
The packages org.hibernate.type.descriptor.java and org.hibernate.type.descriptor.jdbc contain the built-in implementations of JavaType and JdbcType, respectively.

See the User Guide or the package org.hibernate.type for further discussion.

Composite types

A composite type is a type which maps to multiple columns. An example of a composite type is an embeddable object, but this is not the only sort of composite type in Hibernate.

A program may implement the CompositeUserType interface and associate it with a field or property:

Second level cache

When we make a decision to store an entity in the second-level cache, we must decide much more than just whether "to cache or not to cache". Among other considerations:

  • we must assign cache management policies like an expiry timeout, whether to use FIFO-based eviction, whether cached items may be serialized to disk, and
  • we must also take great care in specifying how concurrent access to cached items is managed.

In a multi-user system, these policies always depend quite sensitively on the nature of the given entity type, and cannot reasonably be fixed at a more global level.

With all the above considerations in mind, we strongly recommend the use of the Hibernate-defined annotation Cache to assign entities to the second-level cache.

The JPA-defined Cacheable annotation is almost useless to us, since:

  • it provides no way to specify any information about the nature of the cached entity and how its cache should be managed, and
  • it may not be used to annotate associations.

As an aside, the SharedCacheMode enumeration is even worse: its only sensible values are NONE and ENABLE_SELECTIVE. The options ALL and DISABLE_SELECTIVE fit extremely poorly with the practices advocated above.

Generated values

JPA supports generated identifiers, that is, surrogate primary keys, with four useful built-in types of id generation.

In JPA, an id generator is identified on the basis of a stringly-typed name, and this provides a reasonably natural way to integrate custom generators.

JPA does not define any way to generate the values of other fields or properties of the entity.

Hibernate 6 takes a different route, which is both more typesafe, and much more extensible.

  1. The interfaces BeforeExecutionGenerator and OnExecutionGenerator provide an extremely open-ended way to incorporate custom generators.
  2. The meta-annotations IdGeneratorType and ValueGenerationType may be used to associate a generator with a user-defined annotation. This annotation is an indirection between the generator itself, and the persistent attributes it generates.
  3. This generator annotation may then by used to annotate @Id attributes, @Version attributes, and other @Basic attributes to specify how their values are generated.

This package includes a number built-in generator annotations, including UuidGenerator, CurrentTimestamp, TenantId, Generated, and GeneratedColumn.

Natural ids

The use of surrogate keys is highly recommended, making it much easier to evolve a database schema over time. But every entity should also have a "natural" unique key: a subset of fields which, taken together, uniquely identify an instance of the entity in the business or scientific domain.

The NaturalId annotation is used to identify the natural key of an entity, and urge its use.

The NaturalIdCache annotation enables the use of the second-level cache for when an entity is loaded by natural id. Retrieval by natural id is a very common thing to do, and so the cache can often be helpful.

Filters

Filters are an extremely powerful feature of Hibernate, allowing the definition of parameterized families of filtered "views" of the domain data. They're also easy to use, with the minor caveat that they require the developer to express filtering expressions in native SQL.

  • The FilterDef annotation defines a named filter, declares its parameters, and might specify a filtering expression used by default. There should be exactly one of these annotations per filter name.
  • The Filter annotation is used to identify which entities and associations are affected by the filter, and provide a more specific filtering condition.

Note that a filter has no affect unless it is enabled in a particular session.

Optimistic locking

JPA defines the Version annotation for optimistic locking based on an integral version number or Timestamp. Hibernate allows this annotation to be used with other datetime types including Instant.

A field may be explicitly excluded from optimistic lock checking using @OptimisticLock(excluded=true).

This standard JPA approach is the recommended approach when working with a newly-designed database schema. But when working with a legacy database with tables having no version or update timestamp column, an alternative approach is supported:

  • @OptimisticLocking(ALL) specifies that optimistic lock checking should be done by comparing the values of all columns, and
  • @OptimisticLocking(DIRTY) specifies that optimistic lock checking should be done by checking the values of only the columns which are being set to new values.

For more detail, see OptimisticLocking.

Dialect-specific native SQL

Many annotations in this package allow the specification of native SQL expressions or even complete statements. For example:

A major disadvantage to annotation-based mappings for programs which target multiple databases is that there can be only one source of metadata which must work on every supported database. Fortunately, there's a—slightly inelegant—solution.

The annotations belonging to DialectOverride allow native SQL to be overridden for a particular SQL dialect. For example @DialectOverride.Formula may be used to customize a @Formula for a given version of a given database.