Hibernate.orgCommunity Documentation
Table of Contents
Hibernate supports the enhancement of an application's Java domain model for the purpose of adding various persistence-related capabilities directly into the class, including...
Think of this as partial loading support. Essentially you can tell Hibernate that only part(s) of an entity should be loaded and when the other part(s) should be loaded. Note that this is very much different from proxy-based idea of lazy loading which is entity-centric where the entity's state is loaded at once as needed. With bytecode enhancement, individual attributes or groups of attributes are loaded as needed.
Lazy attributes can be designated to be loaded together; this is called a "lazy group".
By default all singular attributes are part of a single group, meaning that when one lazy singular
attribute is accessed all lazy singular attributes are loaded. Lazy plural attributes, by default,
are each a lazy group by themselves. This behavior is explicitly controllable through the
@org.hibernate.annotations.LazyGroup
annotation.
Example 3.1. @LazyGroup example
@Entity public class Customer { @Id private Integer id; private String name; @Basic( fetch = FetchType.LAZY ) private UUID accountsPayableXrefId; @Lob @Basic( fetch = FetchType.LAZY ) @LazyGroup( "lobs" ) private Blob image; ... }
In the above example we have 2 lazy attributes: accountsPayableXrefId and image. Each is part of a different fetch group (accountsPayableXrefId is part of the default fetch group). Which means that accessing accountsPayableXrefId will not force the loading of image, and vice-versa.
As a hopefully temporary legacy hold-over, it is currently required that all lazy singular
associations (many-to-one and one-to-one) also include @LazyToOne(LazyToOneOption.NO_PROXY)
.
The plan is to relax that requirement later.
Historically Hibernate only supported diff-based dirty calculation for determining which
entities in a persistence context have changed. This essentially means that Hibernate
would keep track of the last known state of an entity in regards to the database (typically
the last read or write). Then, as part of flushing the persistence context, Hibernate would walk
every entity associated with the persistence context and check its current state against that
"last known database state". This is by far the most thorough approach to dirty checking
because it accounts for data-types that can change their internal state
(java.util.Date
is the prime example of this). However, in a persistence
context with a large number of associated entities it can also be a performance-inhibiting
approach.
If your application does not need to care about "internal state changing data-type" use cases, bytecode-enhanced dirty tracking might be a worthwhile alternative to consider, especially in terms of performance. In this approach Hibernate will manipulate the bytecode of your classes to add "dirty tracking" directly to the entity, allowing the entity itself to keep track of which of its attributes have changed. Come flush time, Hibernate simply asks your entity what has changed rather that having to perform the state-diff calculations.
Hibernate strives to keep your application as close to "normal Java usage" (idiomatic Java) as possible. Consider a domain model with a normal Order/LineItem bi-directional association:
Example 3.2. Incorrect normal Java usage
Order order = new Order(); LineItem lineItem = new LineItem(); order.getLineItems().add( lineItem ); // This blows up (NPE) in normal Java usage lineItem.getOrder.getname();
This blows up in normal Java usage. The correct normal Java usage is:
Example 3.3. Correct normal Java usage
Order order = new Order(); LineItem lineItem = new LineItem(); order.getLineItems().add( lineItem ); lineItem.setOrder( order ); // Now this is OK... lineItem.getOrder.getname();
Bytecode-enhanced bi-directional association management makes that first example work by managing the "other side" of a bi-directional association whenever one side is manipulated.
There are two methods to perform bytecode enhancement. It can be done during run time or build time, using one of the provided plugins for build automation tools. Each capability must be enabled independently, using the respective property:
Whether enhancement for lazy attribute loading should be done.
Whether enhancement for self-dirty tracking should be done.
Whether enhancement for bi-directional association management should be done.
Ultimately all enhancement is handled by the org.hibernate.bytecode.enhance.spi.Enhancer
class.
Custom means to enhancement can certainly be crafted on top of Enhancer, but that is beyond the scope of
this guide. Here we will focus on the means Hibernate already exposes for performing these enhancements.
Currently run-time enhancement of the domain model is only supported in managed JPA environments following the JPA defined SPI for performing class transformations. Even then, this support is disabled by default. To enable run-time enhancement, turn on one or more of the following properties in the persistent unit:
Example 3.4. Example persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="Example"> [...] <properties> [...] <property name="hibernate.enhancer.enableLazyInitialization" value="false"/> <property name="hibernate.enhancer.enableDirtyTracking" value="false"/> <property name="hibernate.enhancer.enableAssociationManagement" value="false"/> </properties> </persistence-unit> </persistence>
Also, at the moment only annotated classes are supported for run-time enhancement. Only the classes declared in the persistent unit will be enhanced.
Hibernate provides plugins for Gradle and Maven build tools.
Hibernate provides a Gradle plugin that is capable of providing build-time enhancement of the domain model as they are compiled as part of a Gradle build. To use the plugin a project would first need to apply it.
Example 3.5. Apply the Gradle plugin
ext { hibernateVersion = 'x.y.z.Final' } buildscript { dependencies { classpath "org.hibernate:hibernate-gradle-plugin:$hibernateVersion" } } hibernate { enhance { enableLazyInitialization= false enableDirtyTracking = false enableAssociationManagement = false } }
The configuration that is available is exposed through a registered Gradle DSL extension. The default
value for all configuration settings is false
.
The enhance { }
block is required in order for enhancement to occur. Enhancement
is disabled by default in preparation for additions capabilities (hbm2ddl, etc) in the plugin.
Hibernate provides a Maven plugin capable of providing build-time enhancement of the
domain model as they are compiled as part of a Maven build. The configuration has the same properties
as the Gradle plugin and the default value for all settings is also false
.
The Maven plugin supports one additional configuration settings: failOnError, which controls what happens in case of an error. Default behavior is to fail the build, but it can be set so that only a warning is issued.
Example 3.6. Apply the Maven plugin
<build> <plugins> [...] <plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hibernate-enhance-maven-plugin</artifactId> <version>$hibernateVersion</version> <executions> <execution> <configuration> <failOnError>true</failOnError> <enableLazyInitialization>false</enableLazyInitialization> <enableDirtyTracking>false</enableDirtyTracking> <enableAssociationManagement>false</enableAssociationManagement> </configuration> <goals> <goal>enhance</goal> </goals> </execution> </executions> </plugin> [...] </plugins> </build>