Chapter 5. Hibernate Services

5.1. About Hibernate Services

Services are classes that provide Hibernate with pluggable implementations of various types of functionality. Specifically they are implementations of certain service contract interfaces. The interface is known as the service role; the implementation class is known as the service implementation. Generally speaking, users can plug in alternate implementations of all standard service roles (overriding); they can also define additional services beyond the base set of service roles (extending).

5.2. About Service Contracts

The basic requirement for a service is to implement the marker interface org.hibernate.service.Service. Hibernate uses this internally for some basic type safety.

Optionally, the service can also implement the org.hibernate.service.spi.Startable and org.hibernate.service.spi.Stoppable interfaces to receive notifications of being started and stopped. Another optional service contract is org.hibernate.service.spi.Manageable which marks the service as manageable in Jakarta Management provided the Jakarta Management integration is enabled.

5.3. Types of Service Dependencies

Services are allowed to declare dependencies on other services using either of the following approaches:

@org.hibernate.service.spi.InjectService

Any method on the service implementation class accepting a single parameter and annotated with @InjectService is considered requesting injection of another service.

By default the type of the method parameter is expected to be the service role to be injected. If the parameter type is different than the service role, the serviceRole attribute of the InjectService should be used to explicitly name the role.

By default injected services are considered required, that is the startup will fail if a named dependent service is missing. If the service to be injected is optional, the required attribute of the InjectService should be declared as false. The default is true.

org.hibernate.service.spi.ServiceRegistryAwareService

The second approach is a pull approach where the service implements the optional service interface org.hibernate.service.spi.ServiceRegistryAwareService which declares a single injectServices method.

During startup, Hibernate will inject the org.hibernate.service.ServiceRegistry itself into services which implement this interface. The service can then use the ServiceRegistry reference to locate any additional services it needs.

5.3.1. The Service Registry

5.3.1.1. About the ServiceRegistry

The central service API, aside from the services themselves, is the org.hibernate.service.ServiceRegistry interface. The main purpose of a service registry is to hold, manage and provide access to services.

Service registries are hierarchical. Services in one registry can depend on and utilize services in that same registry as well as any parent registries.

Use org.hibernate.service.ServiceRegistryBuilder to build a org.hibernate.service.ServiceRegistry instance.

Example Using ServiceRegistryBuilder to Create a ServiceRegistry

ServiceRegistryBuilder registryBuilder =
    new ServiceRegistryBuilder( bootstrapServiceRegistry );
    ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry();

5.3.2. Custom Services

5.3.2.1. About Custom Services

Once a org.hibernate.service.ServiceRegistry is built it is considered immutable; the services themselves might accept reconfiguration, but immutability here means adding or replacing services. So another role provided by the org.hibernate.service.ServiceRegistryBuilder is to allow tweaking of the services that will be contained in the org.hibernate.service.ServiceRegistry generated from it.

There are two means to tell a org.hibernate.service.ServiceRegistryBuilder about custom services.

  • Implement a org.hibernate.service.spi.BasicServiceInitiator class to control on-demand construction of the service class and add it to the org.hibernate.service.ServiceRegistryBuilder using its addInitiator method.
  • Just instantiate the service class and add it to the org.hibernate.service.ServiceRegistryBuilder using its addService method.

Either approach is valid for extending a registry, such as adding new service roles, and overriding services, such as replacing service implementations.

Example: Use ServiceRegistryBuilder to Replace an Existing Service with a Custom Service

ServiceRegistryBuilder registryBuilder =
    new ServiceRegistryBuilder(bootstrapServiceRegistry);
registryBuilder.addService(JdbcServices.class, new MyCustomJdbcService());
ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry();

public class MyCustomJdbcService implements JdbcServices{

   @Override
   public ConnectionProvider getConnectionProvider() {
       return null;
   }

   @Override
   public Dialect getDialect() {
       return null;
   }

   @Override
   public SqlStatementLogger getSqlStatementLogger() {
       return null;
   }

   @Override
   public SqlExceptionHelper getSqlExceptionHelper() {
       return null;
   }

   @Override
   public ExtractedDatabaseMetaData getExtractedMetaDataSupport() {
       return null;
   }

   @Override
   public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
       return null;
   }

   @Override
   public ResultSetWrapper getResultSetWrapper() {
       return null;
   }
}

5.3.3. The Boot-Strap Registry

5.3.3.1. About the Boot-strap Registry

The boot-strap registry holds services that absolutely have to be available for most things to work. The main service here is the ClassLoaderService which is a perfect example. Even resolving configuration files needs access to class loading services i.e. resource look ups. This is the root registry, no parent, in normal use.

Instances of boot-strap registries are built using the org.hibernate.service.BootstrapServiceRegistryBuilder class.

Using BootstrapServiceRegistryBuilder

Example: Using BootstrapServiceRegistryBuilder

BootstrapServiceRegistry bootstrapServiceRegistry =
    new BootstrapServiceRegistryBuilder()
    // pass in org.hibernate.integrator.spi.Integrator instances which are not
    // auto-discovered (for whatever reason) but which should be included
    .with(anExplicitIntegrator)
    // pass in a class loader that Hibernate should use to load application classes
    .with(anExplicitClassLoaderForApplicationClasses)
    // pass in a class loader that Hibernate should use to load resources
    .with(anExplicitClassLoaderForResources)
    // see BootstrapServiceRegistryBuilder for rest of available methods
    ...
    // finally, build the bootstrap registry with all the above options
    .build();

5.3.3.2. BootstrapRegistry Services

org.hibernate.service.classloading.spi.ClassLoaderService

Hibernate needs to interact with class loaders. However, the manner in which Hibernate, or any library, should interact with class loaders varies based on the runtime environment that is hosting the application. Application servers, OSGi containers, and other modular class loading systems impose very specific class loading requirements. This service provides Hibernate an abstraction from this environmental complexity. And just as importantly, it does so in a single-swappable-component manner.

In terms of interacting with a class loader, Hibernate needs the following capabilities:

  • the ability to locate application classes
  • the ability to locate integration classes
  • the ability to locate resources, such as properties files and XML files
  • the ability to load java.util.ServiceLoader

    Note

    Currently, the ability to load application classes and the ability to load integration classes are combined into a single load class capability on the service. That may change in a later release.

org.hibernate.integrator.spi.IntegratorService

Applications, add-ons and other modules need to integrate with Hibernate. The previous approach required a component, usually an application, to coordinate the registration of each individual module. This registration was conducted on behalf of each module’s integrator.

This service focuses on the discovery aspect. It leverages the standard Java java.util.ServiceLoader capability provided by the org.hibernate.service.classloading.spi.ClassLoaderService in order to discover implementations of the org.hibernate.integrator.spi.Integrator contract.

Integrators would simply define a file named /META-INF/services/org.hibernate.integrator.spi.Integrator and make it available on the class path.

This file is used by the java.util.ServiceLoader mechanism. It lists, one per line, the fully qualified names of classes which implement the org.hibernate.integrator.spi.Integrator interface.

5.3.4. SessionFactory Registry

While it is best practice to treat instances of all the registry types as targeting a given org.hibernate.SessionFactory, the instances of services in this group explicitly belong to a single org.hibernate.SessionFactory.

The difference is a matter of timing in when they need to be initiated. Generally they need access to the org.hibernate.SessionFactory to be initiated. This special registry is org.hibernate.service.spi.SessionFactoryServiceRegistry.

5.3.4.1. SessionFactory Services

org.hibernate.event.service.spi.EventListenerRegistry

Description
Service for managing event listeners.
Initiator
org.hibernate.event.service.internal.EventListenerServiceInitiator
Implementations
org.hibernate.event.service.internal.EventListenerRegistryImpl

5.3.5. Integrators

The org.hibernate.integrator.spi.Integrator is intended to provide a simple means for allowing developers to hook into the process of building a functioning SessionFactory. The org.hibernate.integrator.spi.Integrator interface defines two methods of interest:

  • integrate allows us to hook into the building process
  • disintegrate allows us to hook into a SessionFactory shutting down.
Note

There is a third method defined in org.hibernate.integrator.spi.Integrator, an overloaded form of integrate, accepting a org.hibernate.metamodel.source.MetadataImplementor instead of org.hibernate.cfg.Configuration.

In addition to the discovery approach provided by the IntegratorService, applications can manually register Integrator implementations when building the BootstrapServiceRegistry.

5.3.5.1. Integrator Use Cases

The main use cases for an org.hibernate.integrator.spi.Integrator are registering event listeners and providing services, see org.hibernate.integrator.spi.ServiceContributingIntegrator.

Example: Registering Event Listeners

public class MyIntegrator implements org.hibernate.integrator.spi.Integrator {

    public void integrate(
            Configuration configuration,
            SessionFactoryImplementor sessionFactory,
            SessionFactoryServiceRegistry serviceRegistry) {
        // As you might expect, an EventListenerRegistry is the thing with which event listeners are registered  It is a
        // service so we look it up using the service registry
        final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService(EventListenerRegistry.class);

        // If you wish to have custom determination and handling of "duplicate" listeners, you would have to add an
        // implementation of the org.hibernate.event.service.spi.DuplicationStrategy contract like this
        eventListenerRegistry.addDuplicationStrategy(myDuplicationStrategy);

        // EventListenerRegistry defines 3 ways to register listeners:
        //     1) This form overrides any existing registrations with
        eventListenerRegistry.setListeners(EventType.AUTO_FLUSH, myCompleteSetOfListeners);
        //     2) This form adds the specified listener(s) to the beginning of the listener chain
        eventListenerRegistry.prependListeners(EventType.AUTO_FLUSH, myListenersToBeCalledFirst);
        //     3) This form adds the specified listener(s) to the end of the listener chain
        eventListenerRegistry.appendListeners(EventType.AUTO_FLUSH, myListenersToBeCalledLast);
    }
}