Movatterモバイル変換


[0]ホーム

URL:


Hibernate ORM 5.3 Integration Guide

Steve Ebersole, Vlad Mihalcea
version 5.3.37.Final
Table of Contents

Preface

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.

Audience

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).

1. Services and Registries

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.

1.1. What is a Service?

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.

1.1.1.Service contracts

The 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.Startable

allows theService impl to be notified that it is being started and about to be put into use.

org.hibernate.service.spi.Stoppable

allows theService impl to be notified that it is being stopped and will be removed from use.

org.hibernate.service.spi.ServiceRegistryAwareService

allows theService to be injected with a reference to the registry that is managing it. SeeService dependencies for more details.

org.hibernate.service.spi.Manageable

marks theService as manageable in JMX provided the JMX integration is enabled. This feature is still incomplete.

Other

The different registry implementations also understand additional optional contracts specific to that registry. For details, see the details for each registry inWhat is aServiceRegistry?.

1.1.2.Service dependencies

Services are allowed to declare dependencies on other services using either of two approaches.

@org.hibernate.service.spi.InjectService

Any 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.ServiceRegistryAwareService

The 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.

1.2. What is aServiceRegistry?

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.

1.3. ServiceBinding

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.

1.4. Types of ServiceRegistries

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.

1.4.1.BootstrapServiceRegistry

Theorg.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).

ClassLoaderService

TheService 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

IntegratorService

TheService 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.

StrategySelector

TheService 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

1.4.2.StandardServiceRegistry

Theorg.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/MultiTenantConnectionProvider

TheService providing Hibernate withConnections as needed.Comes in two distinct (and mutually exclusive) roles:

org.hibernate.engine.jdbc.connections.spi.ConnectionProvider

providesConnections in normal environments

org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider

provides (tenant-specific)Connections in multi-tenant environments

JdbcServices

org.hibernate.engine.jdbc.spi.JdbcServices is an aggregatorService (aService that aggregates other Services) exposing unified functionality around JDBC accessibility.

TransactionCoordinatorBuilder

org.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.

JtaPlatform

When using a JTA-basedTransactionCoordinatorBuilder, theorg.hibernate.engine.transaction.jta.platform.spi.JtaPlatformService provides Hibernate access to the JTATransactionManager andUserTransaction, as well handlingSynchronization registration.

JndiService

Theorg.hibernate.engine.jndi.spi.JndiServiceService is used by Hibernate to interact with JNDI contexts.Hibernate’s defaultJndiService assumes just a singleInitialContext.

RegionFactory

Theorg.hibernate.cache.spi.RegionFactoryService defines the integration with third party cache implementors as second-level caching providers.

SessionFactoryServiceRegistryFactory

org.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.

1.4.3.SessionFactoryServiceRegistry

org.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 theSessionFactoryServiceRegistry.

CurrentlySessionFactoryServiceRegistry holds just four Services.

EventListenerRegistry

org.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)

StatisticsImplementor

org.hibernate.stat.spi.StatisticsImplementor is the SPI portion of the Statistics API; the collector portion, if you will.

NativeQueryInterpreter

org.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.

CacheImplementor

org.hibernate.engine.spi.CacheImplementor provides a way to customize the way Hibernate interacts with the second-level caching implementation.

1.5. Custom Services

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)

1.5.1. CustomService 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.

Example 1. CustomConnectionProvider implementation
import 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.

Example 2. Overriding service implementation viaStandardServiceRegistryBuilder
StandardServiceRegistryBuilder 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.

Example 3.LatestAndGreatestConnectionProviderImplContributor
public 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.

Example 4.META-INF/services/org.hibernate.service.spi.ServiceContributor
fully.qualified.package.LatestAndGreatestConnectionProviderImplContributor1

A 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.

Example 5.LatestAndGreatestConnectionProviderImplContributor variation
public 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.

Example 6. Custom service short-name
StandardServiceRegistryBuilder builder = ...;...builder.applySetting( "hibernate.connection.provider_class", "lag" );...

1.5.2. CustomService 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.

Example 7. TheEventPublishingService service role
public interface EventPublishingService extends Service {    public void publish(Event theEvent);}
Example 8. TheEventPublishingService implementation
public 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();    }}
Example 9. An alternativeEventPublishingService implementation
public 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.

Example 10. TheEventPublishingServiceInitiator
public 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.

Example 11. TheEventPublishingServiceContributor
public 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    }}
Version 5.3.37.Final
Last updated 2024-12-05 11:11:48 UTC

[8]ページ先頭

©2009-2025 Movatter.jp