From a4b5cf4597a0cfed374eaa12559873f5ea194155 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Sat, 21 Mar 2015 13:29:37 -0500 Subject: [PATCH] HHH-9668 - Document new SessionFactory approach and APIs --- .../topical/bootstrap/JpaBootstrapping.adoc | 5 + .../bootstrap/LegacyBootstrapping.adoc | 11 + .../bootstrap/NativeBootstrapping.adoc | 237 ++++++++++++++++++ .../src/main/asciidoc/topical/index.adoc | 4 + .../org/hibernate/boot/MetadataSources.java | 4 +- .../EntityManagerFactoryBuilderImpl.java | 4 + .../ParsedPersistenceXmlDescriptor.java | 5 + .../PersistenceUnitInfoDescriptor.java | 5 + .../boot/spi/PersistenceUnitDescriptor.java | 1 + .../BaseEntityManagerFunctionalTestCase.java | 5 + .../PersistenceUnitDescriptorAdapter.java | 5 + 11 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 documentation/src/main/asciidoc/topical/bootstrap/JpaBootstrapping.adoc create mode 100644 documentation/src/main/asciidoc/topical/bootstrap/LegacyBootstrapping.adoc create mode 100644 documentation/src/main/asciidoc/topical/bootstrap/NativeBootstrapping.adoc diff --git a/documentation/src/main/asciidoc/topical/bootstrap/JpaBootstrapping.adoc b/documentation/src/main/asciidoc/topical/bootstrap/JpaBootstrapping.adoc new file mode 100644 index 0000000000..ceb2bced26 --- /dev/null +++ b/documentation/src/main/asciidoc/topical/bootstrap/JpaBootstrapping.adoc @@ -0,0 +1,5 @@ += Bootstrapping Hibernate JPA +:toc: + +* Spec compliant bootstrapping +* EntityManagerFactoryBuilder \ No newline at end of file diff --git a/documentation/src/main/asciidoc/topical/bootstrap/LegacyBootstrapping.adoc b/documentation/src/main/asciidoc/topical/bootstrap/LegacyBootstrapping.adoc new file mode 100644 index 0000000000..76c890e7a2 --- /dev/null +++ b/documentation/src/main/asciidoc/topical/bootstrap/LegacyBootstrapping.adoc @@ -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 \ No newline at end of file diff --git a/documentation/src/main/asciidoc/topical/bootstrap/NativeBootstrapping.adoc b/documentation/src/main/asciidoc/topical/bootstrap/NativeBootstrapping.adoc new file mode 100644 index 0000000000..ef33a79fc6 --- /dev/null +++ b/documentation/src/main/asciidoc/topical/bootstrap/NativeBootstrapping.adoc @@ -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) \ No newline at end of file diff --git a/documentation/src/main/asciidoc/topical/index.adoc b/documentation/src/main/asciidoc/topical/index.adoc index b70c5e69fc..87c54ff683 100644 --- a/documentation/src/main/asciidoc/topical/index.adoc +++ b/documentation/src/main/asciidoc/topical/index.adoc @@ -8,6 +8,10 @@ NOTE: This is still very much a work in progress. <> is definitely == User Guides +* For information on bootstrapping Hibernate +** For bootstrapping a SessionFactory, see the <> +** For bootstrapping a Hibernate EntityManagerFactory (JPA), see the <> +** For (semi-deprecated) bootstrapping of a SessionFactory using the legacy Configuration approach, see the <> * For information on generated (non-identifier) values, see the <> * For information on logging, see <> * Others coming soon diff --git a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java index 49dbcf0622..35d13e8f98 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java @@ -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 * diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 63ed44e7e2..62632ce987 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -686,6 +686,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } 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 StandardScanOptions( diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ParsedPersistenceXmlDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ParsedPersistenceXmlDescriptor.java index 856c1479c2..8a6bf65700 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ParsedPersistenceXmlDescriptor.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ParsedPersistenceXmlDescriptor.java @@ -195,6 +195,11 @@ public class ParsedPersistenceXmlDescriptor implements org.hibernate.jpa.boot.sp return null; } + @Override + public ClassLoader getTempClassLoader() { + return null; + } + @Override public void pushClassTransformer(List entityClassNames) { // todo : log a message that this is currently not supported... diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceUnitInfoDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceUnitInfoDescriptor.java index 756803120f..0f8dcfd70f 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceUnitInfoDescriptor.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceUnitInfoDescriptor.java @@ -89,6 +89,11 @@ public class PersistenceUnitInfoDescriptor implements PersistenceUnitDescriptor return persistenceUnitInfo.getClassLoader(); } + @Override + public ClassLoader getTempClassLoader() { + return persistenceUnitInfo.getNewTempClassLoader(); + } + @Override public boolean isExcludeUnlistedClasses() { return persistenceUnitInfo.excludeUnlistedClasses(); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/PersistenceUnitDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/PersistenceUnitDescriptor.java index 7f1b8949af..66dac55a9d 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/PersistenceUnitDescriptor.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/PersistenceUnitDescriptor.java @@ -96,6 +96,7 @@ public interface PersistenceUnitDescriptor { public Properties getProperties(); public ClassLoader getClassLoader(); + public ClassLoader getTempClassLoader(); public void pushClassTransformer(List entityClassNames); } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java index 1e971c640e..591bab736c 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java @@ -188,6 +188,11 @@ public abstract class BaseEntityManagerFunctionalTestCase extends BaseUnitTestCa return null; } + @Override + public ClassLoader getTempClassLoader() { + return null; + } + @Override public void pushClassTransformer(List entityClassNames) { } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/PersistenceUnitDescriptorAdapter.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/PersistenceUnitDescriptorAdapter.java index 0427a73a44..7c73c25b9f 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/PersistenceUnitDescriptorAdapter.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/PersistenceUnitDescriptorAdapter.java @@ -120,6 +120,11 @@ public class PersistenceUnitDescriptorAdapter implements PersistenceUnitDescript return Thread.currentThread().getContextClassLoader(); } + @Override + public ClassLoader getTempClassLoader() { + return null; + } + @Override public void pushClassTransformer(List entityClassNames) { }