The two main contracts of Hibernate, org.hibernate.SessionFactory
and org.hibernate.Session
, are both
defined as interfaces which allows for custom implementations to be provided. There are two high-level ways
in which custom implementations can be provided to users. The first is to develop a custom bootstrap API
specific to the custom implementation. The second way is to integrate with the Hibernate bootstrap API.
This guide will cover the second approach.
Implementor contracts
A Hibernate naming convention is that SPI contracts extending API contracts are named with Implementor appended
to the API contract name. For SessionFactory
and Session
that is SessionFactoryImplementor
and
SessionImplementor
respectively. These SPI contracts extra information and functionality needed by internal
components as well as other SPI components. Therefore, custom SessionFactory
and Session
should additionally
implement org.hibernate.engine.spi.SessionFactoryImplementor
and org.hibernate.engine.spi.SessionImplementor
.
See all pertinent JavaDocs for discussions of implementation details.
Integration hooks
org.hibernate.boot.SessionFactoryBuilder
is part of the Hibernate native bootstrap API where we want to configure
the SessionFactory
to be built. Third parties can hook into this process by supplying a
org.hibernate.boot.spi.SessionFactoryBuilderFactory
via the Java ServiceLoader mechanism (see JavaDocs for
java.util.ServiceLoader
if you are unfamiliar with this service discovery mechanism). As you might guess from their
names, a SessionFactoryBuilderFactory
is responsible for creating SessionFactoryBuilder
instances and a
SessionFactoryBuilder
is in turn responsible for creating SessionFactory
instances.
org.hibernate.boot.spi.SessionFactoryOptions
are the options ultimately passed to the SessionFactory
being
built. They represent the choices applied by the user via the SessionFactoryBuilder
contract. Custom integrations
can leverage this and the SessionFactoryBuilder
to also expose custom option setting.
public class CustomSessionFactoryBuilderFactory
implements SessionFactoryBuilderFactory {
@Override
public SessionFactoryBuilder getSessionFactoryBuilder(
MetadataImplementor metadata,
SessionFactoryBuilderImplementor defaultBuilder) {
return new CustomSessionFactoryBuilder( metadata, defaultBuilder );
}
}
public class CustomSessionFactoryBuilder
extends AbstractDelegatingSessionFactoryBuilder {
private final MetadataImplementor metadata;
private final boolean customSetting;
public DelegatingSessionFactoryBuilder(
MetadataImplementor metadata,
SessionFactoryBuilderImplementor delegate) {
super( delegate );
this.metadata = metadata;
// initialize customSetting, maybe based on config settings...
ConfigurationService cfgService = metadata.getMetadataBuildingOptions()
.getServiceRegistry()
.getService( ConfigurationService.class );
this.customSetting = cfgService.getSetting(
"com.acme.domain.custom_setting",
StandardConverters.BOOLEAN,
true
);
}
@Override
public DelegatingSessionFactoryBuilder unwrap() {
return (DelegatingSessionFactoryBuilder) this;
}
public CustomSessionFactoryBuilder applyCustomSetting(boolean enabled) {
this.customSetting = enabled;
return this;
}
@Override
public SessionFactory build() {
CustomSessionFactoryOptions options = new CustomSessionFactoryOptions(
getDelegate().buildSessionFactoryOptions(),
customSetting
);
return new CustomSessionFactory( metadata, options );
}
}
public class CustomSessionFactoryOptions
extends AbstractDelegatingSessionFactoryOptions {
private final boolean customSetting;
public CustomSessionFactoryOptions(
SessionFactoryOptions baseOptions,
boolean customSetting) {
super( baseOptions );
this.customSetting = customSetting;
}
public boolean getCustomSetting() {
return customSetting;
}
}
Users can then build your custom SessionFactory
still using the normal Hibernate bootstrap. In fact,
accepting defaults for your custom settings/options, their code does not even change. Of course they
can also apply selections to your custom settings/options as well:
Metadata metadata = ...;
// The SessionFactory returned here is concretely
// a CustomSessionFactory
SessionFactory sf = metadata.getSessionFactoryBuilder()
.unwrap( CustomSessionFactoryBuilder.class )
.applyCustomSetting( false )
.buildSessionFactory();