Hibernate is anObject/Relational Mapping solution for Java environments.
Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities.It can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC.Hibernate’s design goal is to relieve the developer from 95% of common data persistence-related programming tasks by eliminating the need for manual, hand-crafted data processing using SQL and JDBC.However, unlike many other persistence solutions, Hibernate does not hide the power of SQL from you and guarantees that your investment in relational technology and knowledge is as valid as always.
This guide is for software developers and architects who will be integrating Hibernate with Java EE application servers, Spring framework, caching solutions (e.g. Infinispan, Ehcache, Hazelcast).
Services and registries are newas a formalized concept starting in 4.0.But the functionality provided by the different Services have actually been around in Hibernate much, much longer. What is new is managing them, their lifecycles and dependencies through a lightweight, dedicated container we call aServiceRegistry.The goal of this guide is to describe the design and purpose of theseServices andRegistries, as well as to look at details of their implementations where appropriate.It will also delve into the ways third-party integrators and applications can leverage and customizeServices andRegistries.
A services provides a certain types of functionality, in a pluggable manner.Specifically, they are interfaces defining certain functionality and then implementations of thoseService contract interfaces.The interface is known as theService role; the implementation class is known as theService implementation.The pluggability comes from the fact that theService implementation adheres to contract defined by the interface of theService role and that consumers of theService program to theService role, not the implementation.
Generally speaking, users can plug in alternate implementations of all standardService roles (overriding); they can also define additional services beyond the base set ofService roles (extending).
Let’s look at an example to better define what aService is.Hibernate needs to be able to access JDBCConnections to the database.The way it obtains and releases theseConnections is through theConnectionProvider service.TheService is defined by the interface (service role)org.hibernate.engine.jdbc.connections.spi.ConnectionProvider which declares methods for obtaining and releasing theConnections.There are then multiple implementations of thatService contract, varying in how they actually manage theConnections.
Internally Hibernate always referencesorg.hibernate.engine.jdbc.connections.spi.ConnectionProvider rather than specific implementations in consuming theService (we will get to producing theService later when we talk about registries).Because of that fact, otherConnectionProviderService implementations could easily be plugged in.
There is nothing revolutionary here; programming to interfaces is generally accepted as good programming practice.What’s interesting is theServiceRegistry and the pluggable swapping of the different implementors.
Service contractsThe basic requirement for aService is to implement the marker interfaceorg.hibernate.service.Service.Hibernate uses this internally for some basic type safety.
TheService can also implement a number of optional life-cycle related contracts:
org.hibernate.service.spi.Startableallows theService impl to be notified that it is being started and about to be put into use.
org.hibernate.service.spi.Stoppableallows theService impl to be notified that it is being stopped and will be removed from use.
org.hibernate.service.spi.ServiceRegistryAwareServiceallows theService to be injected with a reference to the registry that is managing it. SeeService dependencies for more details.
org.hibernate.service.spi.Manageablemarks theService as manageable in JMX provided the JMX integration is enabled. This feature is still incomplete.
The different registry implementations also understand additional optional contracts specific to that registry. For details, see the details for each registry inWhat is aServiceRegistry?.
Service dependenciesServices are allowed to declare dependencies on other services using either of two approaches.
@org.hibernate.service.spi.InjectServiceAny method on theService 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 theService role to be injected.If the parameter type is different than theService role, the serviceRole attribute of the@InjectService annotation should be used to explicitly name the role.
By default, injected services are considered required, that is the start up will fail if a named dependentService is missing.If theService to be injected is optional, the required attribute of the@InjectService annotation should be declared asfalse (default istrue).
org.hibernate.service.spi.ServiceRegistryAwareServiceThe second approach is a pull approach where theService implements the optionalService interfaceorg.hibernate.service.spi.ServiceRegistryAwareService which declares a singleinjectServices method.
During startup, Hibernate will inject theorg.hibernate.service.ServiceRegistry itself into services which implement this interface.TheService can then use theServiceRegistry reference to locate any additional services it needs.
ServiceRegistry?AServiceRegistry, at its most basic, hosts and manages Services.Its contract is defined by theorg.hibernate.service.ServiceRegistry interface.
We already gave a basic overview and definition of services.But services have other interesting characteristics as well:
Services have a lifecycle.
They have a scope.
Services might depend on other services.
They need to be produced (choose using one implementation over another).
TheServiceRegistry fulfills all these needs.
In a concise definition, theServiceRegistry acts as aInversion-of-control (IoC) container.
Why not just use an existing IoC framework?The main reason was that this had to be as light-weight and as small of a footprint as possible.The initial design also had called forServices to be swappable at runtime, which unfortunately had to be removed due to performance problems in the proxy-based swapping-solution; the plan is to investigate alternate ways to achieve swap-ability with better performance at a later date.
AService is associated with aServiceRegistry.TheServiceRegistry scopes theService.TheServiceRegistry manages the lifecycle of theService.TheServiceRegistry handles injecting dependencies into theService (actually both a pull and a push/injection approach are supported).ServiceRegistries are also hierarchical, meaning aServiceRegistry can have a parentServiceRegistry.Services in one registry can depend on and utilize services in that same registry as well as any parent registries.
The association of a givenService to a givenServiceRegistry is called a binding and is represented by theorg.hibernate.service.spi.ServiceBinding interface.Furthermore, the specific contract between a ServiceBinding and theServiceRegistry is represented by theorg.hibernate.service.spi.ServiceBinding.ServiceLifecycleOwner interface.
There are two ways aService becomes associated (bound) to aServiceRegistry.
theService can be directly instantiated and then handed to theServiceRegistry
aServiceInitiator can be given to theServiceRegistry (which theServiceRegistry will use if and when theService is needed)
ServiceRegistry implementations register bindings through calls to the overloadedorg.hibernate.service.internal.AbstractServiceRegistryImpl#createServiceBinding method accepting either aService instance or aServiceInitiator instance.
Each specific type of registry defines its ownServiceInitiator specialization.
Currently Hibernate utilizes three differentServiceRegistry implementations forming a hierarchy.Each type is a specialization for the purpose of type-safety, but they add no new functionality.
BootstrapServiceRegistryTheorg.hibernate.boot.registry.BootstrapServiceRegistry holds threeService and is normally built by means of theorg.hibernate.boot.registry.BootstrapServiceRegistryBuilder factory class.The builder gives type safe access to customizing these threeServices.
This registry holds services that absolutely have to be available for most things in Hibernate to work. |
In normal usage, theBootstrapServiceRegistry has no parent.
The services of theBootstrapServiceRegistry cannot be extended (added to) nor overridden (replaced).
ClassLoaderServiceTheService role for thisService isorg.hibernate.boot.registry.classloading.spi.ClassLoaderService.ThisService defines Hibernate’s ability to interact withClassLoaders.The manner in which Hibernate (or any library) should interact withClassLoaders varies based on the runtime environment which is hosting the application.Application servers, OSGi containers, and other modular class loading systems impose very specific class-loading requirements.ThisService provides Hibernate an abstraction from this environmental complexity.And just as important, it does so in a centralized, swappable manner.
The specific capabilities exposed on thisService include:
LocatingClass references by name. This includes application classes as well asintegration classes.
Locating resources (properties files, xml files, etc) asclasspath resources
Interacting withjava.util.ServiceLoader, Java’s ownService provider discovery mechanism
IntegratorServiceTheService role for thisService isorg.hibernate.integrator.spi.IntegratorService.Applications, third-party integrators and others all need to integrate with Hibernate. Historically this used to require something (usually the application) to coordinate registering the pieces of each integration needed on behalf of each integration. Theorg.hibernate.integrator.spi.Integrator contract formalized this "integration SPI". The IntegratorService manages all known integrators.
The concept of "Integrator" is still being actively defined and developed. Expect changes in these SPIs. |
There are two ways an integrator becomes known.
The integrator may be manually registered by callingBootstrapServiceRegistryBuilder#with(Integrator)
The integrator may be discovered, leveraging the standard JavaServiceLoader capability provided by theClassLoaderService.Integrators would simply define a file named/META-INF/services/org.hibernate.integrator.spi.Integrator and make it available on the classpath.ServiceLoader covers the format of this file in detail, but essentially it lists classes by fully-qualified name that implementIntegrator one per line.
StrategySelectorTheService role for thisService isorg.hibernate.boot.registry.selector.spi.StrategySelector.Think of this as theshort naming service.Historically to configure Hibernate users would often need to give fully-qualified name references to internal Hibernate classes.Of course, this has caused lots of problems as we refactor internal code and move these classes around into different package structures.Enter the concept of short-naming, using a well defined and well knownshort name for the strategy/implementation class.
The short name mappings in thisService can be managed, even by applications and integrators which can be very powerful.For more information on this aspect, see:
BootstrapServiceRegistryBuilder#applyStrategySelector
BootstrapServiceRegistryBuilder#applyStrategySelectors
org.hibernate.boot.registry.selector.StrategyRegistrationProvider viaServiceLoader discovery
StrategySelector#registerStrategyImplementor /StrategySelector#unRegisterStrategyImplementor
StandardServiceRegistryTheorg.hibernate.boot.registry.StandardServiceRegistry defines the main HibernateServiceRegistry, building on theBootstrapServiceRegistry which is its parent.This registry is generally built using theorg.hibernate.boot.registry.StandardServiceRegistryBuilder class.By default, it holds most of theServices used by Hibernate.For the full list ofServices typically held in theStandardServiceRegistry, see the source code oforg.hibernate.service.StandardServiceInitiators.
In normal usage, the parent of the StandardServiceRegistry is the BootstrapServiceRegistry.
The services of the StandardServiceRegistry can be extended (added to) and overridden (replaced).
ConnectionProvider/MultiTenantConnectionProviderTheService providing Hibernate withConnections as needed.Comes in two distinct (and mutually exclusive) roles:
org.hibernate.engine.jdbc.connections.spi.ConnectionProviderprovidesConnections in normal environments
org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProviderprovides (tenant-specific)Connections in multi-tenant environments
JdbcServicesorg.hibernate.engine.jdbc.spi.JdbcServices is an aggregatorService (aService that aggregates other Services) exposing unified functionality around JDBC accessibility.
TransactionCoordinatorBuilderorg.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder is used by Hibernate to integrate with and underlying transaction system.It is responsible for buildingorg.hibernate.resource.transaction.spi.TransactionCoordinator instances for use by each HibernateSession.
JtaPlatformWhen using a JTA-basedTransactionCoordinatorBuilder, theorg.hibernate.engine.transaction.jta.platform.spi.JtaPlatformService provides Hibernate access to the JTATransactionManager andUserTransaction, as well handlingSynchronization registration.
JndiServiceTheorg.hibernate.engine.jndi.spi.JndiServiceService is used by Hibernate to interact with JNDI contexts.Hibernate’s defaultJndiService assumes just a singleInitialContext.
RegionFactoryTheorg.hibernate.cache.spi.RegionFactoryService defines the integration with third party cache implementors as second-level caching providers.
SessionFactoryServiceRegistryFactoryorg.hibernate.service.spi.SessionFactoryServiceRegistryFactory is aService that acts as a factory for building the third type ofServiceRegistry (the SessionFactoryServiceRegistry) which we will discuss next.I opted for thefactory as service approach because in the current design there is really not a good exposed hook-in spot for when theSessionFactoryServiceRegistry needs to be built.
SessionFactoryServiceRegistryorg.hibernate.service.spi.SessionFactoryServiceRegistry is the third standard HibernateServiceRegistry.SessionFactoryServiceRegistry is designed to holdServices which need access to theSessionFactory.
Typically its parent registry is theStandardServiceRegistry.
Integrators, as it stands in 4.x, operate on the |
CurrentlySessionFactoryServiceRegistry holds just four Services.
EventListenerRegistryorg.hibernate.event.service.spi.EventListenerRegistry is the mainService managed in theSessionFactoryServiceRegistry.The is theService that manages all of Hibernate’s event listeners.A major use-case forIntegrators is to alter the listener registry.
If doing custom listener registration, it is important to understand theorg.hibernate.event.service.spi.DuplicationStrategy and its effect on registration.The basic idea is to tell Hibernate:
what makes a listener a duplicate
how to handle duplicate registrations (error, first wins, last wins)
StatisticsImplementororg.hibernate.stat.spi.StatisticsImplementor is the SPI portion of the Statistics API; the collector portion, if you will.
NativeQueryInterpreterorg.hibernate.engine.query.spi.NativeQueryInterpreter is theService Hibernate uses for interpreting native queries.Exists as aService mainly so that integrations such as OGM can override it.
CacheImplementororg.hibernate.engine.spi.CacheImplementor provides a way to customize the way Hibernate interacts with the second-level caching implementation.
So far we have focused on the Hibernate provided services.But applications and integrations can provide their own services as well, either
providing a new implementation of a standardService (overriding)
providing a whole newService role (extending)
Service Implementations (overriding)We discussed swappability ofService implementations above.Lets look at an example in practice.For the sake of illustration, lets say that we have developed a newConnectionProvider integrating with the wonderful new latest-and-greatest connection pooling library. Let’s look at the steps necessary to make that happen.
The first step is to develop the actual integration by implementing theConnectionProvider contract.
ConnectionProvider implementationimport java.lang.Override;public class LatestAndGreatestConnectionProviderImpl implements ConnectionProvider, Startable, Stoppable, Configurable { private LatestAndGreatestPoolBuilder lagPoolBuilder; private LatestAndGreatestPool lagPool; private boolean available = false; @Override public void configure(Map configurationValues) { // extract our config from the settings map lagPoolBuilder = buildBuilder( configurationValues ); } @Override public void start() { // start the underlying pool lagPool = lagPoolBuilder.buildPool(); available = true; } @Override public void stop() { available = false; // stop the underlying pool lagPool.shutdown(); } @Override public Connection getConnection() throws SQLException { if ( !available ) { throwException( "LatestAndGreatest ConnectionProvider not available for use" ) } return lagPool.borrowConnection(); } @Override public void closeConnection(Connection conn) throws SQLException { if ( !available ) { warn( "LatestAndGreatest ConnectionProvider not available for use" ) } if ( conn == null ) { return; } lagPool.releaseConnection( conn ); } ...}At this point we have a decision about how to integrate this newConnectionProvider into Hibernate.As you might guess, there are multiple ways.
As a first option, we might just require that the code bootstrapping theStandardServiceRegistry do the integration.
StandardServiceRegistryBuilderStandardServiceRegistryBuilder builder = ...;...builder.addService( ConnectionProvider.class, new LatestAndGreatestConnectionProviderImpl());...A second option, if ourLatestAndGreatestConnectionProviderImpl should always be used, would be to provide aorg.hibernate.service.spi.ServiceContributor implementation as well to handle the integration on the users behalf.
LatestAndGreatestConnectionProviderImplContributorpublic class LatestAndGreatestConnectionProviderImplContributor1 implements ServiceContributor { @Override public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) { serviceRegistryBuilder.addService( ConnectionProvider.class, new LatestAndGreatestConnectionProviderImpl() ); }}We still need to be able to tell Hibernate to perform this integration for us.To do that we leverage Java’sServiceLoader.When building theStandardServiceRegistry, Hibernate will look for JDKService providers of typeorg.hibernate.service.spi.ServiceContributor and automatically integrate them.We discussed this behavior above. Here we’d define a classpath resource namedMETA-INF/services/org.hibernate.service.spi.ServiceContributor.This file will have just a single line naming our impl.
META-INF/services/org.hibernate.service.spi.ServiceContributorfully.qualified.package.LatestAndGreatestConnectionProviderImplContributor1A third option, if we simply want to make ourLatestAndGreatestConnectionProviderImpl available as a configuration choice, we would again use aServiceContributor but in a slightly different way.
LatestAndGreatestConnectionProviderImplContributor variationpublic class LatestAndGreatestConnectionProviderImplContributor implements ServiceContributor { @Override public void contribute( standardserviceregistrybuilder serviceregistrybuilder) { // here we will register a short-name for our service strategy strategyselector selector = serviceregistrybuilder .getbootstrapserviceregistry(). .getservice( strategyselector.class ); selector.registerstrategyimplementor( connectionprovider.class, "lag" latestandgreatestconnectionproviderimpl.class ); }}That all allows the application to pick ourLatestAndGreatestConnectionProviderImpl by a short-name.
StandardServiceRegistryBuilder builder = ...;...builder.applySetting( "hibernate.connection.provider_class", "lag" );...Service Roles (extending)We can also have theServiceRegistry host custom services (completely newService roles).As an example, let’s say our application publishes Hibernate events to a JMS Topic and that we want to leverage the HibernateServiceRegistry to host aService representing our publishing of events.So, we will expand theServiceRegistry to host this completely newService role for us and manage its lifecycle.
EventPublishingService service rolepublic interface EventPublishingService extends Service { public void publish(Event theEvent);}EventPublishingService implementationpublic class EventPublishingServiceImpl implements EventPublishingService, Configurable, Startable, Stoppable, ServiceRegistryAwareService { private ServiceRegistryImplementor serviceRegistry; private String jmsConnectionFactoryName; private String destinationName; private Connection jmsConnection; private Session jmsSession; private MessageProducer publisher; @Override public void injectServices(ServiceRegistryImplementor serviceRegistry) { this.serviceRegistry = serviceRegistry; } public void configure(Map configurationValues) { this.jmsConnectionFactoryName = configurationValues .get( JMS_CONNECTION_FACTORY_NAME_SETTING ); this.destinationName = configurationValues .get( JMS_DESTINATION_NAME_SETTING ); } @Override public void start() { final JndiService jndiService = serviceRegistry .getService( JndiService.class ); final ConnectionFactory jmsConnectionFactory = jndiService .locate( jmsConnectionFactoryName ); this.jmsConnection = jmsConnectionFactory.createConnection(); this.jmsSession = jmsConnection.createSession( true, Session.AUTO_ACKNOWLEDGE ); final Destination destination = jndiService.locate( destinationName ); this.publisher = jmsSession.createProducer( destination ); } @Override public void publish(Event theEvent) { publisher.send( theEvent ); } @Override public void stop() { publisher.close(); jmsSession.close(); jmsConnection.close(); }}EventPublishingService implementationpublic class DisabledEventPublishingServiceImpl implements EventPublishingService { public static DisabledEventPublishingServiceImpl INSTANCE = new DisabledEventPublishingServiceImpl(); private DisabledEventPublishingServiceImpl() { } @Override public void publish(Event theEvent) { // nothing to do... }}Because we have alternative implementations, it is a good idea to develop an initiator as well that can choose between them at runtime.
EventPublishingServiceInitiatorpublic class EventPublishingServiceInitiator implements StandardServiceInitiator<EventPublishingService> { public static EventPublishingServiceInitiator INSTANCE = new EventPublishingServiceInitiator(); public static final String ENABLE_PUBLISHING_SETTING = "com.acme.EventPublishingService.enabled"; @Override public Class<R> getServiceInitiated() { return EventPublishingService.class; } @Override public R initiateService( Map configurationValues, ServiceRegistryImplementor registry) { final boolean enabled = extractBoolean( configurationValues, ENABLE_PUBLISHING_SETTING ); if ( enabled ) { return new EventPublishingServiceImpl(); } else { return DisabledEventPublishingServiceImpl.INSTANCE; } } ...}We could have the application register theEventPublishingServiceInitiator with theStandardServiceRegistryBuilder, but it is much nicer to write aServiceContributor to handle this for the application.
EventPublishingServiceContributorpublic class EventPublishingServiceContributor implements ServiceContributor { @Override public void contribute(StandardServiceRegistryBuilder builder) { builder.addinitiator( eventpublishingserviceinitiator.instance ); // if we wanted to allow other strategies (e.g. a jms // queue publisher) we might also register short names // here with the strategyselector. the initiator would // then need to accept the strategy as a config setting }}