HHH-12589 - Add support for registering custom SQL functions when bootstrapping via JPA
This commit is contained in:
parent
de44e4180d
commit
4a8951a6b4
|
@ -1028,6 +1028,9 @@ Like a `PersisterClassResolver`, the `PersisterFactory` can be used to customize
|
|||
`*hibernate.service.allow_crawling*` (e.g. `true` (default value) or `false`)::
|
||||
Crawl all available service bindings for an alternate registration of a given Hibernate `Service`.
|
||||
|
||||
`*hibernate.metadata_builder_contributor*` (e.g. The instance, the class or the fully qualified class name of an https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/jpa/boot/spi/MetadataBuilderContributor.html[`MetadataBuilderContributor`])::
|
||||
Used to define a instance, the class or the fully qualified class name of an https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/jpa/boot/spi/MetadataBuilderContributor.html[`MetadataBuilderContributor`] which can be used to configure the `MetadataBuilder` when bootstrapping via the JPA `EntityManagerFactory`.
|
||||
|
||||
[[configurations-misc]]
|
||||
=== Miscellaneous properties
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
[[bootstrap]]
|
||||
== Bootstrap
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/bootstrap
|
||||
:boot-spi-sourcedir: ../../../../../../../hibernate-core/src/test/java/org/hibernate/boot/spi
|
||||
:extrasdir: extras
|
||||
|
||||
org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
The term bootstrapping refers to initializing and starting a software component.
|
||||
In Hibernate, we are specifically talking about the process of building a fully functional `SessionFactory` instance or `EntityManagerFactory` instance, for JPA.
|
||||
The process is very different for each.
|
||||
|
@ -19,143 +22,6 @@ Instead, we focus here on the API calls needed to perform the bootstrapping.
|
|||
During the bootstrap process, you might want to customize Hibernate behavior so make sure you check the <<appendices/Configurations.adoc#configurations,Configurations>> section as well.
|
||||
====
|
||||
|
||||
[[bootstrap-jpa]]
|
||||
=== JPA Bootstrapping
|
||||
|
||||
Bootstrapping Hibernate as a JPA provider can be done in a JPA-spec compliant manner or using a proprietary bootstrapping approach.
|
||||
The standardized approach has some limitations in certain environments, but aside from those, it is *highly* recommended that you use JPA-standardized bootstrapping.
|
||||
|
||||
[[bootstrap-jpa-compliant]]
|
||||
==== JPA-compliant bootstrapping
|
||||
|
||||
In JPA, we are ultimately interested in bootstrapping a `javax.persistence.EntityManagerFactory` instance.
|
||||
The JPA specification defines two primary standardized bootstrap approaches depending on how the application intends to access the `javax.persistence.EntityManager` instances from an `EntityManagerFactory`.
|
||||
|
||||
It uses the terms _EE_ and _SE_ for these two approaches, but those terms are very misleading in this context.
|
||||
What the JPA spec calls EE bootstrapping implies the existence of a container (EE, OSGi, etc), who'll manage and inject the persistence context on behalf of the application.
|
||||
What it calls SE bootstrapping is everything else. We will use the terms container-bootstrapping and application-bootstrapping in this guide.
|
||||
|
||||
For compliant container-bootstrapping, the container will build an `EntityManagerFactory` for each persistent-unit defined in the `META-INF/persistence.xml` configuration file
|
||||
and make that available to the application for injection via the `javax.persistence.PersistenceUnit` annotation or via JNDI lookup.
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceUnit-example]]
|
||||
.Injecting the default `EntityManagerFactory`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceUnit-example]
|
||||
----
|
||||
====
|
||||
|
||||
Or, in case you have multiple Persistence Units (e.g. multiple `persistence.xml` configuration files),
|
||||
you can inject a specific `EntityManagerFactory` by Unit name:
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceUnit-configurable-example]]
|
||||
.Injecting a specific `EntityManagerFactory`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceUnit-configurable-example]
|
||||
----
|
||||
====
|
||||
|
||||
The `META-INF/persistence.xml` file looks as follows:
|
||||
|
||||
[[bootstrap-jpa-compliant-persistence-xml-example]]
|
||||
.META-INF/persistence.xml configuration file
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/persistence.xml[]
|
||||
----
|
||||
====
|
||||
|
||||
For compliant application-bootstrapping, rather than the container building the `EntityManagerFactory` for the application, the application builds the `EntityManagerFactory` itself using the `javax.persistence.Persistence` bootstrap class.
|
||||
The application creates an `EntityManagerFactory` by calling the `createEntityManagerFactory` method:
|
||||
|
||||
[[bootstrap-jpa-compliant-EntityManagerFactory-example]]
|
||||
.Application bootstrapped `EntityManagerFactory`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-EntityManagerFactory-example]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you don't want to provide a `persistence.xml` configuration file, JPA allows you to provide all the configuration options in a
|
||||
http://docs.oracle.com/javaee/7/api/javax/persistence/spi/PersistenceUnitInfo.html[PersistenceUnitInfo] implementation and call
|
||||
https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/jpa/HibernatePersistenceProvider.html#createContainerEntityManagerFactory-javax.persistence.spi.PersistenceUnitInfo-java.util.Map-[HibernatePersistenceProvider.html#createContainerEntityManagerFactory].
|
||||
====
|
||||
|
||||
To inject the default Persistence Context, you can use the http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceContext.html[`@PersistenceContext`] annotation.
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceContext-example]]
|
||||
.Inject the default `EntityManager`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceContext-example, indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
To inject a specific Persistence Context,
|
||||
you can use the http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceContext.html[`@PersistenceContext`] annotation,
|
||||
and you can even pass `EntityManager`-specific properties using the
|
||||
http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceProperty.html[`@PersistenceProperty`] annotation.
|
||||
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceContext-configurable-example]]
|
||||
.Inject a configurable `EntityManager`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceContext-configurable-example, indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you would like additional details on accessing and using `EntityManager` instances, sections 7.6 and 7.7 of the JPA 2.1 specification cover container-managed and application-managed `EntityManagers`, respectively.
|
||||
====
|
||||
|
||||
[[bootstrap-jpa-xml-files]]
|
||||
==== Externalizing XML mapping files
|
||||
|
||||
JPA offers two mapping options:
|
||||
|
||||
- annotations
|
||||
- XML mappings
|
||||
|
||||
Although annotations are much more common, there are projects were XML mappings are preferred.
|
||||
You can even mix annotations and XML mappings so that you can override annotation mappings with XML configurations that can be easily changed without recompiling the project source code.
|
||||
This is possible because if there are two conflicting mappings, the XML mappings takes precedence over its annotation counterpart.
|
||||
|
||||
The JPA specifications requires the XML mappings to be located on the class path:
|
||||
|
||||
[quote, Section 8.2.1.6.2 of the JPA 2.1 Specification]
|
||||
____
|
||||
An object/relational mapping XML file named `orm.xml` may be specified in the `META-INF` directory in the root of the persistence unit or in the `META-INF` directory of any jar file referenced by the `persistence.xml`.
|
||||
|
||||
Alternatively, or in addition, one or more mapping files may be referenced by the mapping-file elements of the persistence-unit element. These mapping files may be present anywhere on the class path.
|
||||
____
|
||||
|
||||
Therefore, the mapping files can reside in the application jar artifacts, or they can be stored in an external folder location with the cogitation that that location be included in the class path.
|
||||
|
||||
Hibernate is more lenient in this regard so you can use any external location even outside of the application configured class path.
|
||||
|
||||
[[bootstrap-jpa-compliant-persistence-xml-external-mappings-example]]
|
||||
.META-INF/persistence.xml configuration file for external XML mappings
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/persistence-external.xml[]
|
||||
----
|
||||
====
|
||||
|
||||
In the `persistence.xml` configuration file above, the `orm.xml` XML file containing all JPA entity mappings is located in the `/etc/opt/app/mappings/` folder.
|
||||
|
||||
[[bootstrap-native]]
|
||||
=== Native Bootstrapping
|
||||
|
||||
|
@ -315,3 +181,166 @@ The bootstrapping API is quite flexible, but in most cases it makes the most sen
|
|||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-native-SessionFactoryBuilder-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[bootstrap-jpa]]
|
||||
=== JPA Bootstrapping
|
||||
|
||||
Bootstrapping Hibernate as a JPA provider can be done in a JPA-spec compliant manner or using a proprietary bootstrapping approach.
|
||||
The standardized approach has some limitations in certain environments, but aside from those, it is *highly* recommended that you use JPA-standardized bootstrapping.
|
||||
|
||||
[[bootstrap-jpa-compliant]]
|
||||
==== JPA-compliant bootstrapping
|
||||
|
||||
In JPA, we are ultimately interested in bootstrapping a `javax.persistence.EntityManagerFactory` instance.
|
||||
The JPA specification defines two primary standardized bootstrap approaches depending on how the application intends to access the `javax.persistence.EntityManager` instances from an `EntityManagerFactory`.
|
||||
|
||||
It uses the terms _EE_ and _SE_ for these two approaches, but those terms are very misleading in this context.
|
||||
What the JPA spec calls EE bootstrapping implies the existence of a container (EE, OSGi, etc), who'll manage and inject the persistence context on behalf of the application.
|
||||
What it calls SE bootstrapping is everything else. We will use the terms container-bootstrapping and application-bootstrapping in this guide.
|
||||
|
||||
For compliant container-bootstrapping, the container will build an `EntityManagerFactory` for each persistent-unit defined in the `META-INF/persistence.xml` configuration file
|
||||
and make that available to the application for injection via the `javax.persistence.PersistenceUnit` annotation or via JNDI lookup.
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceUnit-example]]
|
||||
.Injecting the default `EntityManagerFactory`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceUnit-example]
|
||||
----
|
||||
====
|
||||
|
||||
Or, in case you have multiple Persistence Units (e.g. multiple `persistence.xml` configuration files),
|
||||
you can inject a specific `EntityManagerFactory` by Unit name:
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceUnit-configurable-example]]
|
||||
.Injecting a specific `EntityManagerFactory`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceUnit-configurable-example]
|
||||
----
|
||||
====
|
||||
|
||||
The `META-INF/persistence.xml` file looks as follows:
|
||||
|
||||
[[bootstrap-jpa-compliant-persistence-xml-example]]
|
||||
.META-INF/persistence.xml configuration file
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/persistence.xml[]
|
||||
----
|
||||
====
|
||||
|
||||
For compliant application-bootstrapping, rather than the container building the `EntityManagerFactory` for the application, the application builds the `EntityManagerFactory` itself using the `javax.persistence.Persistence` bootstrap class.
|
||||
The application creates an `EntityManagerFactory` by calling the `createEntityManagerFactory` method:
|
||||
|
||||
[[bootstrap-jpa-compliant-EntityManagerFactory-example]]
|
||||
.Application bootstrapped `EntityManagerFactory`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-EntityManagerFactory-example]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you don't want to provide a `persistence.xml` configuration file, JPA allows you to provide all the configuration options in a
|
||||
http://docs.oracle.com/javaee/7/api/javax/persistence/spi/PersistenceUnitInfo.html[`PersistenceUnitInfo`] implementation and call
|
||||
https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/jpa/HibernatePersistenceProvider.html#createContainerEntityManagerFactory-javax.persistence.spi.PersistenceUnitInfo-java.util.Map-[`HibernatePersistenceProvider.html#createContainerEntityManagerFactory`].
|
||||
====
|
||||
|
||||
To inject the default Persistence Context, you can use the http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceContext.html[`@PersistenceContext`] annotation.
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceContext-example]]
|
||||
.Inject the default `EntityManager`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceContext-example, indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
To inject a specific Persistence Context,
|
||||
you can use the http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceContext.html[`@PersistenceContext`] annotation,
|
||||
and you can even pass `EntityManager`-specific properties using the
|
||||
http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceProperty.html[`@PersistenceProperty`] annotation.
|
||||
|
||||
|
||||
[[bootstrap-jpa-compliant-PersistenceContext-configurable-example]]
|
||||
.Inject a configurable `EntityManager`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BootstrapTest.java[tags=bootstrap-jpa-compliant-PersistenceContext-configurable-example, indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you would like additional details on accessing and using `EntityManager` instances, sections 7.6 and 7.7 of the JPA 2.1 specification cover container-managed and application-managed `EntityManagers`, respectively.
|
||||
====
|
||||
|
||||
[[bootstrap-jpa-xml-files]]
|
||||
==== Externalizing XML mapping files
|
||||
|
||||
JPA offers two mapping options:
|
||||
|
||||
- annotations
|
||||
- XML mappings
|
||||
|
||||
Although annotations are much more common, there are projects were XML mappings are preferred.
|
||||
You can even mix annotations and XML mappings so that you can override annotation mappings with XML configurations that can be easily changed without recompiling the project source code.
|
||||
This is possible because if there are two conflicting mappings, the XML mappings takes precedence over its annotation counterpart.
|
||||
|
||||
The JPA specifications requires the XML mappings to be located on the class path:
|
||||
|
||||
[quote, Section 8.2.1.6.2 of the JPA 2.1 Specification]
|
||||
____
|
||||
An object/relational mapping XML file named `orm.xml` may be specified in the `META-INF` directory in the root of the persistence unit or in the `META-INF` directory of any jar file referenced by the `persistence.xml`.
|
||||
|
||||
Alternatively, or in addition, one or more mapping files may be referenced by the mapping-file elements of the persistence-unit element. These mapping files may be present anywhere on the class path.
|
||||
____
|
||||
|
||||
Therefore, the mapping files can reside in the application jar artifacts, or they can be stored in an external folder location with the cogitation that that location be included in the class path.
|
||||
|
||||
Hibernate is more lenient in this regard so you can use any external location even outside of the application configured class path.
|
||||
|
||||
[[bootstrap-jpa-compliant-persistence-xml-external-mappings-example]]
|
||||
.META-INF/persistence.xml configuration file for external XML mappings
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/persistence-external.xml[]
|
||||
----
|
||||
====
|
||||
|
||||
In the `persistence.xml` configuration file above, the `orm.xml` XML file containing all JPA entity mappings is located in the `/etc/opt/app/mappings/` folder.
|
||||
|
||||
[[bootstrap-jpa-metadata]]
|
||||
==== Configuring the `SessionFactory` `Metadata` via the JPA bootstrap
|
||||
|
||||
As previously seen, the Hibernate native bootstrap mechanism allows you to customize a great variety of configurations which are passed via the `Metadata` object.
|
||||
|
||||
When you Hibernate as a JPA provider, the `EntityManagerFactory` is backed by a `SessionFactory`. For this reason, you might still want to use the `Metadata` object to pass various settings which cannot be supplied via the standard Hibernate <<appendices/Configurations.adoc, configuration settings>>.
|
||||
|
||||
For this reason, you can use the
|
||||
https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/boot/spi/MetadataBuilderContributor.html[`MetadataBuilderContributor`] class as you can see in the following examples.
|
||||
|
||||
[[bootstrap-jpa-compliant-MetadataBuilderContributor-example]]
|
||||
.Implementing a `MetadataBuilderContributor`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{boot-spi-sourcedir}/metadatabuildercontributor/SqlFunctionMetadataBuilderContributor.java[tags=bootstrap-jpa-compliant-MetadataBuilderContributor-example]
|
||||
----
|
||||
====
|
||||
|
||||
The above `MetadataBuilderContributor` is used to register a `SqlFuction` which is not defined by the currently running Hibernate `Dialect`, but which we need to reference in our JPQL queries.
|
||||
|
||||
By having access to the
|
||||
https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/boot/MetadataBuilder.html[`MetadataBuilder`] class that's used by the underlying `SessionFactory`, the JPA bootstrap becomes just as flexible as the Hibernate native bootstrap mechanism.
|
||||
|
||||
You can then pass the custom `MetadataBuilderContributor` via the `hibernate.metadata_builder_contributor` configuration property as explained in the <<appendices/Configurations.adoc#configurations-bootstrap, configuration chapter>>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi;
|
||||
|
||||
import org.hibernate.boot.MetadataBuilder;
|
||||
|
||||
/**
|
||||
* A bootstrap process hook for contributing settings to {@link MetadataBuilder}.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*
|
||||
* @since 5.3
|
||||
*/
|
||||
public interface MetadataBuilderContributor {
|
||||
/**
|
||||
* Perform the process of contributing to MetadataSources.
|
||||
*
|
||||
* @param metadataBuilder The {@link MetadataBuilder}, to which to contribute.
|
||||
*/
|
||||
void contribute(MetadataBuilder metadataBuilder);
|
||||
}
|
|
@ -24,7 +24,6 @@ import javax.persistence.PersistenceException;
|
|||
import javax.persistence.spi.PersistenceUnitTransactionType;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.SessionFactoryObserver;
|
||||
import org.hibernate.boot.CacheRegionDefinition;
|
||||
|
@ -46,6 +45,7 @@ import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence;
|
|||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.boot.registry.selector.StrategyRegistrationProvider;
|
||||
import org.hibernate.boot.registry.selector.spi.StrategySelector;
|
||||
import org.hibernate.boot.spi.MetadataBuilderContributor;
|
||||
import org.hibernate.boot.spi.MetadataBuilderImplementor;
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
|
||||
|
@ -132,6 +132,11 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
|||
*/
|
||||
public static final String TYPE_CONTRIBUTORS = "hibernate.type_contributors";
|
||||
|
||||
/**
|
||||
* Names a {@link MetadataBuilderImplementor}
|
||||
*/
|
||||
public static final String METADATA_BUILDER_CONTRIBUTOR = "hibernate.metadata_builder_contributor";
|
||||
|
||||
/**
|
||||
* Names a Jandex {@link Index} instance to use.
|
||||
*/
|
||||
|
@ -229,6 +234,9 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
|||
metamodelBuilder.getBootstrapContext()
|
||||
);
|
||||
|
||||
applyMetadataBuilderContributor();
|
||||
|
||||
|
||||
withValidatorFactory( configurationValues.get( org.hibernate.cfg.AvailableSettings.JPA_VALIDATION_FACTORY ) );
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -253,6 +261,51 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
|||
metamodelBuilder.applyTempClassLoader( null );
|
||||
}
|
||||
|
||||
private void applyMetadataBuilderContributor() {
|
||||
|
||||
Object metadataBuilderContributorSetting = configurationValues.get( METADATA_BUILDER_CONTRIBUTOR );
|
||||
|
||||
if ( metadataBuilderContributorSetting == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetadataBuilderContributor metadataBuilderContributor = null;
|
||||
Class<? extends MetadataBuilderContributor> metadataBuilderContributorImplClass = null;
|
||||
|
||||
if ( metadataBuilderContributorSetting instanceof MetadataBuilderContributor ) {
|
||||
metadataBuilderContributor = (MetadataBuilderContributor) metadataBuilderContributorSetting;
|
||||
}
|
||||
else if ( metadataBuilderContributorSetting instanceof Class ) {
|
||||
metadataBuilderContributorImplClass = (Class<? extends MetadataBuilderContributor>) metadataBuilderContributorSetting;
|
||||
}
|
||||
else if ( metadataBuilderContributorSetting instanceof String ) {
|
||||
final ClassLoaderService classLoaderService = standardServiceRegistry.getService( ClassLoaderService.class );
|
||||
|
||||
metadataBuilderContributorImplClass = classLoaderService.classForName( (String) metadataBuilderContributorSetting );
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"The provided " + METADATA_BUILDER_CONTRIBUTOR + " setting value [" + metadataBuilderContributorSetting + "] is not supported!"
|
||||
);
|
||||
}
|
||||
|
||||
if ( metadataBuilderContributorImplClass != null ) {
|
||||
try {
|
||||
metadataBuilderContributor = metadataBuilderContributorImplClass.newInstance();
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"The MetadataBuilderContributor class [" + metadataBuilderContributorImplClass + "] could not be instantiated!",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( metadataBuilderContributor != null ) {
|
||||
metadataBuilderContributor.contribute( metamodelBuilder );
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// temporary!
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-12589" )
|
||||
public abstract class AbstractSqlFunctionMetadataBuilderContributorTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Employee.class,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put(
|
||||
EntityManagerFactoryBuilderImpl.METADATA_BUILDER_CONTRIBUTOR,
|
||||
matadataBuilderContributor()
|
||||
);
|
||||
}
|
||||
|
||||
protected abstract Object matadataBuilderContributor();
|
||||
|
||||
final Employee employee = new Employee();
|
||||
|
||||
@Override
|
||||
protected void afterEntityManagerFactoryBuilt() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
employee.id = 1L;
|
||||
employee.username = "user@acme.com";
|
||||
|
||||
entityManager.persist( employee );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
String result = (String) entityManager.createQuery(
|
||||
"select INSTR(e.username,'@acme.com') " +
|
||||
"from Employee e " +
|
||||
"where " +
|
||||
" e.id = :employeeId")
|
||||
.setParameter( "employeeId", employee.id )
|
||||
.getSingleResult();
|
||||
|
||||
assertEquals( "5", result );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Employee")
|
||||
public static class Employee {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import org.hibernate.boot.MetadataBuilder;
|
||||
import org.hibernate.boot.spi.MetadataBuilderContributor;
|
||||
import org.hibernate.dialect.function.StandardSQLFunction;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
//tag::bootstrap-jpa-compliant-MetadataBuilderContributor-example[]
|
||||
public class SqlFunctionMetadataBuilderContributor
|
||||
implements MetadataBuilderContributor {
|
||||
|
||||
@Override
|
||||
public void contribute(MetadataBuilder metadataBuilder) {
|
||||
metadataBuilder.applySqlFunction(
|
||||
"instr", new StandardSQLFunction( "instr", StandardBasicTypes.STRING )
|
||||
);
|
||||
}
|
||||
}
|
||||
//end::bootstrap-jpa-compliant-MetadataBuilderContributor-example[]
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-12589" )
|
||||
public class SqlFunctionMetadataBuilderContributorClassNameTest
|
||||
extends AbstractSqlFunctionMetadataBuilderContributorTest {
|
||||
|
||||
@Override
|
||||
protected Object matadataBuilderContributor() {
|
||||
return SqlFunctionMetadataBuilderContributor.class.getName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-12589" )
|
||||
public class SqlFunctionMetadataBuilderContributorClassTest
|
||||
extends AbstractSqlFunctionMetadataBuilderContributorTest {
|
||||
|
||||
@Override
|
||||
protected Object matadataBuilderContributor() {
|
||||
return SqlFunctionMetadataBuilderContributor.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
||||
import org.hibernate.testing.FailureExpected;
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-12589" )
|
||||
public class SqlFunctionMetadataBuilderContributorIllegalArgumentTest
|
||||
extends AbstractSqlFunctionMetadataBuilderContributorTest {
|
||||
|
||||
@Override
|
||||
protected Object matadataBuilderContributor() {
|
||||
return new Object();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEntityManagerFactory() {
|
||||
try {
|
||||
super.buildEntityManagerFactory();
|
||||
|
||||
fail("Should throw exception!");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertTrue( e.getMessage().startsWith( "The provided hibernate.metadata_builder_contributor setting value" ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void test() {
|
||||
try {
|
||||
super.test();
|
||||
|
||||
fail("Should throw exception!");
|
||||
}
|
||||
catch (Exception expected) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-12589" )
|
||||
public class SqlFunctionMetadataBuilderContributorIllegalClassArgumentTest
|
||||
extends AbstractSqlFunctionMetadataBuilderContributorTest {
|
||||
|
||||
@Override
|
||||
protected Object matadataBuilderContributor() {
|
||||
return this.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEntityManagerFactory() {
|
||||
try {
|
||||
super.buildEntityManagerFactory();
|
||||
|
||||
fail("Should throw exception!");
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
assertTrue( e.getMessage().contains( "cannot be cast to org.hibernate.boot.spi.MetadataBuilderContributor" ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void test() {
|
||||
try {
|
||||
super.test();
|
||||
|
||||
fail("Should throw exception!");
|
||||
}
|
||||
catch (Exception expected) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-12589" )
|
||||
public class SqlFunctionMetadataBuilderContributorInstanceTest
|
||||
extends AbstractSqlFunctionMetadataBuilderContributorTest {
|
||||
|
||||
@Override
|
||||
protected Object matadataBuilderContributor() {
|
||||
return new SqlFunctionMetadataBuilderContributor();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.spi.metadatabuildercontributor;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.util.ExceptionUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-12589" )
|
||||
public class SqlFunctionMissingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Employee.class,
|
||||
};
|
||||
}
|
||||
|
||||
final Employee employee = new Employee();
|
||||
|
||||
@Override
|
||||
protected void afterEntityManagerFactoryBuilt() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
employee.id = 1L;
|
||||
employee.username = "user@acme.com";
|
||||
|
||||
entityManager.persist( employee );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Number result = (Number) entityManager.createQuery(
|
||||
"select INSTR(e.username,'@') " +
|
||||
"from Employee e " +
|
||||
"where " +
|
||||
" e.id = :employeeId")
|
||||
.setParameter( "employeeId", employee.id )
|
||||
.getSingleResult();
|
||||
|
||||
fail("Should throw exception!");
|
||||
} );
|
||||
}
|
||||
catch (Exception expected) {
|
||||
assertTrue( ExceptionUtil.rootCause( expected ).getMessage().contains( "No data type for node: org.hibernate.hql.internal.ast.tree.MethodNod" ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Employee")
|
||||
public static class Employee {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue