HHH-9668 - Document new SessionFactory approach and APIs

This commit is contained in:
Steve Ebersole 2015-03-21 13:29:37 -05:00
parent 37b4aeb7c9
commit a4b5cf4597
11 changed files with 285 additions and 1 deletions

View File

@ -0,0 +1,5 @@
= Bootstrapping Hibernate JPA
:toc:
* Spec compliant bootstrapping
* EntityManagerFactoryBuilder

View File

@ -0,0 +1,11 @@
= Legacy Bootstrapping
:toc:
The legacy way to bootstrap a `SessionFactory` is via the `org.hibernate.cfg.Configuration` object.
`Configuration` represents, essentially, a single point for specifying all aspects of building
the `SessionFactory`: everything from settings, to mappings, to strategies, etc.
However there is a huge draw back to this approach which led to the development of the new
approach discussed below.
todo : discuss the timing issues

View File

@ -0,0 +1,237 @@
= Native Bootstrapping
:toc:
This guide discusses the process of bootstrapping a Hibernate `org.hibernate.SessionFactory`. It also
discusses the ways in which applications and integrators can hook-in to and affect that process. This
bootstrapping process is defined in 2 distinct steps. The first step is the building of a ServiceRegistry
holding the services Hibernate will need at bootstrap- and run-time. The second step is the building of
a Metadata object representing the mapping information for the application's model and its mapping to
the database.
NOTE : Prior to version 5.0 applications bootstrapped a `SessionFactory` is via the
`org.hibernate.cfg.Configuration` object. That approach is still supported in a slightly limited manner.
See the _Legacy Bootstrapping_ guide for details.
== Building the ServiceRegistry
Actually we are concerned with building 2 different ServiceRegistries:
* `org.hibernate.boot.registry.BootstrapServiceRegistry`
* `org.hibernate.boot.registry.StandardServiceRegistry`
Each of these is built from a builder, `org.hibernate.boot.registry.BootstrapServiceRegistryBuilder`
and `org.hibernate.boot.registry.StandardServiceRegistryBuilder` respectively.
NOTE: For more information on ServiceRegistries in general, see the _Services and Registries_ guide.
=== BootstrapServiceRegistry
The `BootstrapServiceRegistry` is intended to hold services that Hibernate needs at both bootstrap and run time.
This boils down to 3 services:
* `ClassLoaderService` - which controls how Hibernate interacts with ClassLoaders
* `IntegratorService` - which controls the management ands discovery of `org.hibernate.integrator.spi.Integrator` instances.
* `StrategySelector` - which control how Hibernate resolves implementations of various strategy
contracts. This is a very powerful service, but a full discussion of it is beyond the scope
of this guide.
If you are ok with the default behavior of Hibernate in regards to these BootstrapServiceRegistry services
(which is quite often the case, especially in SE environments) building the BootstrapServiceRegistry can be skipped.
If you wish to alter how the `BootstrapServiceRegistry` is built, you would use the `BootstrapServiceRegistryBuilder`:
[[bootstrap-registry-builder-example]]
.Building a BootstrapServiceRegistry
====
[source, JAVA]
----
BootstrapServiceRegistryBuilder bootstrapRegistryBuilder = new BootstrapServiceRegistryBuilder();
// add a special ClassLoader
bootstrapRegistryBuilder.with( mySpecialClassLoader );
// manually add an Integrator
bootstrapRegistryBuilder.with( mySpecialIntegrator );
...
BootstrapServiceRegistry bootstrapRegistry = bootstrapRegistryBuilder.build();
----
====
The services of the `BootstrapServiceRegistry` cannot be extended (added to) nor overridden (replaced).
=== StandardServiceRegistry
The services of the `StandardServiceRegistry` may be extended and overridden.
A `StandardServiceRegistry` is built through the `StandardServiceRegistryBuilder` which can be constructed in
one of 2 ways:
[[standard-registry-builder-example1]]
.Building a StandardServiceRegistryBuilder with a supplied BootstrapServiceRegistry
====
[source, JAVA]
----
BootstrapServiceRegistry bootstrapRegistry = ...;
StandardServiceRegistryBuilder standardRegistryBuilder = new StandardServiceRegistryBuilder( bootstrapRegistry );
----
====
[[standard-registry-builder-example2]]
.Building a StandardServiceRegistryBuilder without a supplied BootstrapServiceRegistry
====
[source, JAVA]
----
StandardServiceRegistryBuilder standardRegistryBuilder = new StandardServiceRegistryBuilder();
----
====
The second form will create a `BootstrapServiceRegistry` on the fly with default behavior.
A `StandardServiceRegistry` is also highly configurable via the `StandardServiceRegistryBuilder` API. See the
`StandardServiceRegistryBuilder` javadocs for full details. Some specific methods of interest:
[[standard-registry-builder-example3]]
.Configuring StandardServiceRegistryBuilder
====
[source, JAVA]
----
StandardServiceRegistryBuilder standardRegistryBuilder = ...;
// load some properties via resource lookup
standardRegistryBuilder.loadProperties( "org/hibernate/example/MyProperties.properties" );
// configure the registry from a resource lookup for a cfg.xml config file
standardRegistryBuilder.configure( "org/hibernate/example/MyCfg.xml" );
// apply a random setting
standardRegistryBuilder.applySetting( "myProp", "some value" );
// apply a service initiator
standardRegistryBuilder.addInitiator( new CustomServiceInitiator() );
// apply a service impl
standardRegistryBuilder.addService( SomeCustomService.class, new SomeCustomServiceImpl() );
// and finally build the StandardServiceRegistry
StandardServiceRegistry standardRegistry = standardRegistryBuilder.build();
----
====
== Building the Metadata
The `org.hibernate.boot.Metadata` object contains the parsed representations of an application's
domain model and its mapping to a database. The first thing we obviously need to build a parsed
representation is the source information to be parsed. This is the purpose of
`org.hibernate.boot.MetadataSources`.
[[MetadataSources-example]]
.Configuring a MetadataSources
====
[source, JAVA]
----
MetadataSources sources = new MetadataSources( standardRegistry );
// alternatively, we can build the MetadataSources without passing
// a service registry, in which case it will build a default
// BootstrapServiceRegistry to use
// MetadataSources sources = new MetadataSources();
// add a class using JPA/Hibernate annotations for mapping
sources.addAnnotatedClass( MyEntity.class );
// add the name of a class using JPA/Hibernate annotations for mapping.
// differs from above in that accessing the Class is deferred which is
// important if using runtime bytecode-enhancement
sources.addAnnotatedClassName( "org.hibernate.example.Customer" );
// Adds the named hbm.xml resource as a source: which performs the
// classpath lookup and parses the XML
sources.addResource( "org/hibernate/example/Order.hbm.xml" );
// Adds the named JPA orm.xml resource as a source: which performs the
// classpath lookup and parses the XML
sources.addResource( "org/hibernate/example/Product.orm.xml" );
----
====
`MetadataSources` has many other methods as well; explore its API and javadocs for more information. Also,
all methods on `MetadataSources` allow for chaining should you prefer that style.
[[MetadataSources-chaining-example]]
.Configuring a MetadataSources with method chaining
====
[source, JAVA]
----
MetadataSources sources = new MetadataSources( standardRegistry )
.addAnnotatedClass( MyEntity.class )
.addAnnotatedClassName( "org.hibernate.example.Customer" )
.addResource( "org/hibernate/example/Order.hbm.xml" )
.addResource( "org/hibernate/example/Product.orm.xml" );
----
====
Once we have the sources of mapping information defined, we need to build the `Metadata` object. If you are
ok with the default behavior in building the `Metadata` (or if relying on a `MetadataBuilderContributor` - see below)
then you can simply call `MetadataSources#buildMetadata`.
NOTE : Notice that a ServiceRegistry can be passed at a number of points in this bootstrapping process. The suggested
approach is to build a `StandardServiceRegistry` yourself and pass that along to the `MetadataSources` constructor.
From there, `MetadataBuilder`, `Metadata`, `SessionFactoryBuilder` and `SessionFactory` will all pick up that
same `StandardServiceRegistry`.
However, if you wish to adjust the process of building `Metadata` from `MetadataSources` you will need to use
the `MetadataBuilder` as obtained via `MetadataSources#getMetadataBuilder`. `MetadataBuilder` allows a lot of control
over the `Metadata` building process. See its javadocs for full details.
[[MetadataBuilder-example]]
.Building Metadata via MetadataBuilder
====
[source, JAVA]
----
MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
// Use the JPA-compliant implicit naming strategy
metadataBuilder.with( ImplicitNamingStrategyJpaCompliantImpl.INSTANCE );
// specify the schema name to use for tables, etc when none is explicitly specified
metadataBuilder.withImplicitSchemaName( "my_default_schema" );
Metadata metadata = metadataBuilder.build();
----
====
== Building the SessionFactory
Much like we've discussed above, if you are ok with the default behavior of building a `SessionFactory`
from a `Metadata` reference, you can simply call `Metadata#buildSessionFactory`. However, if you would like to
adjust that building process you will need to use `SessionFactoryBuilder` as obtained via
`Metadata#getSessionFactoryBuilder`. See the `SessionFactoryBuilder` javadocs for details of the control it allows
over the `SessionFactory` building process.
[[SessionFactoryBuilder-example]]
.Building SessionFactory via SessionFactoryBuilder
====
[source, JAVA]
----
SessionFactoryBuilder sessionFactoryBuilder = metadata.getSessionFactoryBuilder();
// Supply an SessionFactory-level Interceptor
sessionFactoryBuilder.with( new MySessionFactoryInterceptor() );
// Add a custom observer
sessionFactoryBuilder.add( new MySessionFactoryObserver() );
// Apply a CDI BeanManager (for JPA event listeners)
sessionFactoryBuilder.withBeanManager( getBeanManagerFromSomewhere() );
SessionFactory sessionFactory = sessionFactoryBuilder.build();
----
====
== Putting It All Together
== Integration Points
=== Integrator
=== ServiceContributor
=== TypeContributor
=== MetadataSourcesContributor
=== MetadataBuilderContributor
=== SessionFactoryBuilderContributor (todo)

View File

@ -8,6 +8,10 @@ NOTE: This is still very much a work in progress. <<helping,Help>> is definitely
== User Guides == User Guides
* For information on bootstrapping Hibernate
** For bootstrapping a SessionFactory, see the <<bootstrap/NativeBootstrapping.adoc#,Native Bootstrapping Guide>>
** For bootstrapping a Hibernate EntityManagerFactory (JPA), see the <<bootstrap/JpaBootstrapping.adoc#,JPA Bootstrapping Guide>>
** For (semi-deprecated) bootstrapping of a SessionFactory using the legacy Configuration approach, see the <<bootstrap/LegacyBootstrapping.adoc#,Legacy Bootstrapping Guide>>
* For information on generated (non-identifier) values, see the <<generated/GeneratedValues.adoc#,Generated Values Guide>> * For information on generated (non-identifier) values, see the <<generated/GeneratedValues.adoc#,Generated Values Guide>>
* For information on logging, see <<logging/Logging.adoc#,Logging Guide>> * For information on logging, see <<logging/Logging.adoc#,Logging Guide>>
* Others coming soon * Others coming soon

View File

@ -207,7 +207,9 @@ public class MetadataSources implements Serializable {
} }
/** /**
* Read metadata from the annotations attached to the given class. * Read metadata from the annotations attached to the given class. The important
* distinction here is that the {@link Class} will not be accessed until later
* which is important for on-the-fly bytecode-enhancement
* *
* @param annotatedClassName The name of a class containing annotations * @param annotatedClassName The name of a class containing annotations
* *

View File

@ -686,6 +686,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
} }
private void populate(MetadataBuilder metamodelBuilder, MergedSettings mergedSettings, StandardServiceRegistry ssr) { private void populate(MetadataBuilder metamodelBuilder, MergedSettings mergedSettings, StandardServiceRegistry ssr) {
if ( persistenceUnit.getTempClassLoader() != null ) {
metamodelBuilder.with( persistenceUnit.getTempClassLoader() );
}
metamodelBuilder.with( new StandardJpaScanEnvironmentImpl( persistenceUnit ) ); metamodelBuilder.with( new StandardJpaScanEnvironmentImpl( persistenceUnit ) );
metamodelBuilder.with( metamodelBuilder.with(
new StandardScanOptions( new StandardScanOptions(

View File

@ -195,6 +195,11 @@ public class ParsedPersistenceXmlDescriptor implements org.hibernate.jpa.boot.sp
return null; return null;
} }
@Override
public ClassLoader getTempClassLoader() {
return null;
}
@Override @Override
public void pushClassTransformer(List<String> entityClassNames) { public void pushClassTransformer(List<String> entityClassNames) {
// todo : log a message that this is currently not supported... // todo : log a message that this is currently not supported...

View File

@ -89,6 +89,11 @@ public class PersistenceUnitInfoDescriptor implements PersistenceUnitDescriptor
return persistenceUnitInfo.getClassLoader(); return persistenceUnitInfo.getClassLoader();
} }
@Override
public ClassLoader getTempClassLoader() {
return persistenceUnitInfo.getNewTempClassLoader();
}
@Override @Override
public boolean isExcludeUnlistedClasses() { public boolean isExcludeUnlistedClasses() {
return persistenceUnitInfo.excludeUnlistedClasses(); return persistenceUnitInfo.excludeUnlistedClasses();

View File

@ -96,6 +96,7 @@ public interface PersistenceUnitDescriptor {
public Properties getProperties(); public Properties getProperties();
public ClassLoader getClassLoader(); public ClassLoader getClassLoader();
public ClassLoader getTempClassLoader();
public void pushClassTransformer(List<String> entityClassNames); public void pushClassTransformer(List<String> entityClassNames);
} }

View File

@ -188,6 +188,11 @@ public abstract class BaseEntityManagerFunctionalTestCase extends BaseUnitTestCa
return null; return null;
} }
@Override
public ClassLoader getTempClassLoader() {
return null;
}
@Override @Override
public void pushClassTransformer(List<String> entityClassNames) { public void pushClassTransformer(List<String> entityClassNames) {
} }

View File

@ -120,6 +120,11 @@ public class PersistenceUnitDescriptorAdapter implements PersistenceUnitDescript
return Thread.currentThread().getContextClassLoader(); return Thread.currentThread().getContextClassLoader();
} }
@Override
public ClassLoader getTempClassLoader() {
return null;
}
@Override @Override
public void pushClassTransformer(List<String> entityClassNames) { public void pushClassTransformer(List<String> entityClassNames) {
} }