This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.
Hibernate Setup¶
Introduction¶
Hibernate is an implementation of the JPA specification, however we use the Hibernate API directly in many cases (instead of using the JPA API in the jakarta.persistence package), mostly because we need many custom Hibernate features to be able to provide exactly the same behaviour as the old Persistence API did.
Build the SessionFactory¶
The first step is to initialize a SessionFactory.
This is done by the SessionFactoryProvider,
which is a spring @Configuration
class.
The Hibernate bootstrapping process is documented here.
The SessionFactory should only be used internally by the persist/core
module. If other modules would use it directly, they could bypass the security layer.
Participate in the bootstrap process¶
It is possible for other modules to apply custom configuration options during the building of the session factory by contributing a HibernateBootstrapContribution. This contribution provides several methods to participate in various steps of the bootstrapping process. It’s also possible to provide a priority to control the execution order of the different contributions. This can for example be used for registering custom java types.
The main configuration is done by HibernateCoreBootstrapContribution.
ContributionClassLoaderService¶
The ContributionClassLoaderService is a custom ClassLoaderService which makes it easy to contribute services at runtime and to avoid having to use the ServiceLoader API used by the default implementation.
Bootstrap steps¶
Register custom user extensions¶
Several user extensions are registered with the ContributionClassLoaderService:
For each custom java type a TypeContributor is contributed. There are some default types (for example
binary
ordatetime
) that are always registered, but other modules can contribute java types as well (see Custom java types).FieldGenerator contributions (fields that are set automatically by the framework, like the create/update timestamps and users) (see Automatically generated values).
Generate entity classes¶
Entity classes are generated based on the entity models and then registered with the provided MetadataSources.
Apply Hibernate properties¶
The next step is to apply the Hibernate configuration settings. The interface HibernatePropertiesProvider defines some common properties in a default method.
The only implementation (HibernatePropertiesProviderImpl) adds the connection options to the default properties. These are read from the application properties. The properties need to be transformed to a different format as Hibernate uses different options than HikariCP.
The ToccoDialectResolver is a custom
DialectResolver, which makes sure that our custom dialects are used
by hibernate. It is configured using the hibernate.dialect_resolvers
property.
Injecting service factories¶
We use a custom implementation of PersisterFactory. This allows (manually) injecting services or contributions into a custom persister. Without using a custom factory, Hibernate just calls the default constructor.
Hibernate interceptor¶
A custom Hibernate Interceptor is registered as well. ValidationInterceptor runs the entity validation before the changes are flushed to the database
JDBC function registration¶
All JdbcFunction are registered with the SessionFactoryBuilder.
Event listener registration¶
Multiple Hibernate listeners (see EventType) are registered:
ExtendedInitializeCollectionEventListener initializes collections using a custom query which includes security and business unit predicates. See Collections.
CustomFlushEntityEventListener handles custom after commit events (see EntityListener)
AfterCommitListener and CustomFlushEntityEventListener are responsible for firing after commit events (see Listeners).
Startup time improvements¶
Hibernate completely initializes every entity during the construction of the session factory. Among many other things this includes:
A ProxyFactory for every entity (required to instantiate lazily loaded entity proxies). These are currently based on byte buddy and take some time to initialize, especially for hundreds of entities.
This makes sense for a production environment, but during development a quicker startup time is more important because usually only a fraction of all entities is used. It therefore makes more sense to initialize these objects on the fly when they are needed for the first time.
Similarly, the ToccoByteBuddyBytecodeProvider does not initialize the ProxyFactory until it is needed.
To reduce memory consumption a custom BatchLoaderFactory (CustomBatchLoaderFactory is contributed. As we have custom AbstractCollectionPersister the CollectionLoader are probably never used. Therefore we don’t initialize them until necessary, as they take up a lot of memory.