Merge branch master into wip/6.0

This commit is contained in:
Andrea Boriero 2021-01-14 17:38:24 +01:00
commit 90f18b3137
66 changed files with 3331 additions and 140 deletions

View File

@ -21,19 +21,18 @@ While we try to keep requirements for contributing to a minimum, there are a few
we ask that you mind. we ask that you mind.
For code contributions, these guidelines include: For code contributions, these guidelines include:
* respect the project code style - find templates for [Eclipse](https://community.jboss.org/docs/DOC-16649) * respect the project code style - find templates for [IntelliJ IDEA](https://community.jboss.org/docs/DOC-15468) or [Eclipse](https://community.jboss.org/docs/DOC-16649)
and [IntelliJ IDEA](https://community.jboss.org/docs/DOC-15468)
* have a corresponding JIRA issue and the key for this JIRA issue should be used in the commit message * have a corresponding JIRA issue and the key for this JIRA issue should be used in the commit message
* have a set of appropriate tests. For bug reports, the tests reproduce the initial reported bug * have a set of appropriate tests. For bug reports, the tests reproduce the initial reported bug
and illustrates that the solution actually fixes the bug. For features/enhancements, the and illustrates that the solution actually fixes the bug. For features/enhancements, the
tests illustrate the feature working as intended. In both cases the tests are incorporated into tests illustrate the feature working as intended. In both cases the tests are incorporated into
the project to protect against regressions. the project to protect against regressions
* if applicable, documentation is updated to reflect the introduced changes * if applicable, documentation is updated to reflect the introduced changes
* the code compiles and the tests pass (`./gradlew clean build`) * the code compiles and the tests pass (`./gradlew clean build`)
For documentation contributions, mainly just respect the project code style, especially in regards For documentation contributions, mainly just respect the project code style, especially in regards
to use of tabs - as mentioned above, code style templates are available for both Eclipse and IntelliJ to use of tabs - as mentioned above, code style templates are available for both IntelliJ IDEA and Eclipse
IDEA IDEs. Ideally these contributions would also have a corresponding JIRA issue, although this IDEs. Ideally these contributions would also have a corresponding JIRA issue, although this
is less necessary for documentation contributions. is less necessary for documentation contributions.
@ -42,12 +41,12 @@ is less necessary for documentation contributions.
If you are just getting started with Git, GitHub and/or contributing to Hibernate via If you are just getting started with Git, GitHub and/or contributing to Hibernate via
GitHub there are a few pre-requisite steps to follow: GitHub there are a few pre-requisite steps to follow:
* Make sure you have a [Hibernate JIRA account](https://hibernate.atlassian.net) * make sure you have a [Hibernate JIRA account](https://hibernate.atlassian.net)
* Make sure you have a [GitHub account](https://github.com/signup/free) * make sure you have a [GitHub account](https://github.com/signup/free)
* [Fork](https://help.github.com/articles/fork-a-repo) the Hibernate repository. As discussed in * [fork](https://help.github.com/articles/fork-a-repo) the Hibernate repository. As discussed in
the linked page, this also includes: the linked page, this also includes:
* [Set](https://help.github.com/articles/set-up-git) up your local git install * [set up your local git install](https://help.github.com/articles/set-up-git)
* Clone your fork * clone your fork
* See the wiki pages for setting up your IDE, whether you use * See the wiki pages for setting up your IDE, whether you use
[IntelliJ IDEA](https://community.jboss.org/wiki/ContributingToHibernateUsingIntelliJ) [IntelliJ IDEA](https://community.jboss.org/wiki/ContributingToHibernateUsingIntelliJ)
or [Eclipse](https://community.jboss.org/wiki/ContributingToHibernateUsingEclipse)<sup>(1)</sup>. or [Eclipse](https://community.jboss.org/wiki/ContributingToHibernateUsingEclipse)<sup>(1)</sup>.
@ -59,7 +58,7 @@ Create a [topic branch](http://git-scm.com/book/en/Git-Branching-Branching-Workf
on which you will work. The convention is to incorporate the JIRA issue key in the name of this branch, on which you will work. The convention is to incorporate the JIRA issue key in the name of this branch,
although this is more of a mnemonic strategy than a hard-and-fast rule - but doing so helps: although this is more of a mnemonic strategy than a hard-and-fast rule - but doing so helps:
* remember what each branch is for * remember what each branch is for
* isolate the work from other contributions you may be working on. * isolate the work from other contributions you may be working on
_If there is not already a JIRA issue covering the work you want to do, create one._ _If there is not already a JIRA issue covering the work you want to do, create one._
@ -69,17 +68,17 @@ on the JIRA HHH-123 : `git checkout -b HHH-123 master`
## Code ## Code
Do yo thing! Do your thing!
## Commit ## Commit
* Make commits of logical units. * make commits of logical units
* Be sure to use the JIRA issue key in the commit message. This is how JIRA will pick * be sure to **use the JIRA issue key** in the commit message. This is how JIRA will pick
up the related commits and display them on the JIRA issue. up the related commits and display them on the JIRA issue
* Make sure you have added the necessary tests for your changes. * make sure you have added the necessary tests for your changes
* Run _all_ the tests to assure nothing else was accidentally broken. * run _all_ the tests to assure nothing else was accidentally broken
* Make sure your source does not violate the checkstyles. * make sure your source does not violate the _checkstyles_
_Prior to committing, if you want to pull in the latest upstream changes (highly _Prior to committing, if you want to pull in the latest upstream changes (highly
appreciated btw), please use rebasing rather than merging. Merging creates appreciated btw), please use rebasing rather than merging. Merging creates
@ -87,10 +86,9 @@ appreciated btw), please use rebasing rather than merging. Merging creates
## Submit ## Submit
* Push your changes to the topic branch in your fork of the repository. * push your changes to the topic branch in your fork of the repository
* Initiate a [pull request](http://help.github.com/articles/creating-a-pull-request) * initiate a [pull request](http://help.github.com/articles/creating-a-pull-request)
* Update the JIRA issue, adding a comment including a link to the created pull request * update the JIRA issue by providing the PR link in the **Pull Request** column on the right
_if the JIRA key was not used in the commit message_.
It is important that this topic branch on your fork: It is important that this topic branch on your fork:
@ -98,9 +96,9 @@ It is important that this topic branch on your fork:
* be isolated to just the work on this one JIRA issue, or multiple issues if they are * be isolated to just the work on this one JIRA issue, or multiple issues if they are
related and also fixed/implemented by this work. The main point is to not push related and also fixed/implemented by this work. The main point is to not push
commits for more than one PR to a single branch - GitHub PRs are linked to commits for more than one PR to a single branch - GitHub PRs are linked to
a branch rather than specific commits. a branch rather than specific commits
* remain until the PR is closed. Once the underlying branch is deleted the corresponding * remain until the PR is closed. Once the underlying branch is deleted the corresponding
PR will be closed, if not already, and the changes will be lost. PR will be closed, if not already, and the changes will be lost
# Notes # Notes
<sup>(1)</sup> Gradle `eclipse` plugin is no longer supported, so the recommended way to import the project in your IDE is with the proper IDE tools/plugins. Don't try to run `./gradlew clean eclipse --refresh-dependencies` from the command line as you'll get an error because `eclipse` no longer exists <sup>(1)</sup> Gradle `eclipse` plugin is no longer supported, so the recommended way to import the project in your IDE is with the proper IDE tools/plugins. Don't try to run `./gradlew clean eclipse --refresh-dependencies` from the command line as you'll get an error because `eclipse` no longer exists

View File

@ -1,6 +1,7 @@
# Keep all these properties in sync unless you know what you are doing! # Keep all these properties in sync unless you know what you are doing!
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8
toolchain.compiler.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 # Needs add-opens because of https://github.com/gradle/gradle/issues/15538
toolchain.compiler.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 --add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
toolchain.javadoc.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 toolchain.javadoc.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8
toolchain.launcher.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 toolchain.launcher.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8

View File

@ -251,7 +251,7 @@ if ( gradle.ext.javaToolchainEnabled ) {
} }
// Configure JVM Options // Configure JVM Options
jvmArgs.addAll( getProperty( 'toolchain.launcher.jvmargs' ).toString().split( ' ' ) ) jvmArgs( getProperty( 'toolchain.launcher.jvmargs' ).toString().split( ' ' ) )
// Display version of Java tools // Display version of Java tools
doFirst { doFirst {

View File

@ -23,7 +23,7 @@ ext {
elVersion = '3.0.1-b09' elVersion = '3.0.1-b09'
cdiVersion = '2.0' cdiVersion = '2.0'
weldVersion = '3.0.0.Final' weldVersion = '3.1.5.Final'
javassistVersion = '3.27.0-GA' javassistVersion = '3.27.0-GA'
byteBuddyVersion = '1.10.17' byteBuddyVersion = '1.10.17'
@ -49,6 +49,8 @@ ext {
antlrVersion = '4.8-1' antlrVersion = '4.8-1'
micrometerVersion = '1.6.1'
libraries = [ libraries = [
// Ant // Ant
ant: 'org.apache.ant:ant:1.8.2', ant: 'org.apache.ant:ant:1.8.2',
@ -155,6 +157,7 @@ ext {
vibur: "org.vibur:vibur-dbcp:25.0", vibur: "org.vibur:vibur-dbcp:25.0",
agroal_api: "io.agroal:agroal-api:${agroalVersion}", agroal_api: "io.agroal:agroal-api:${agroalVersion}",
agroal_pool: "io.agroal:agroal-pool:${agroalVersion}", agroal_pool: "io.agroal:agroal-pool:${agroalVersion}",
micrometer: "io.micrometer:micrometer-core:1.6.1",
atomikos: "com.atomikos:transactions:4.0.6", atomikos: "com.atomikos:transactions:4.0.6",
atomikos_jta: "com.atomikos:transactions-jta:4.0.6", atomikos_jta: "com.atomikos:transactions-jta:4.0.6",

View File

@ -263,6 +263,15 @@ task generateEnversStaticMetamodel(
test { test {
systemProperty 'file.encoding', 'utf-8' systemProperty 'file.encoding', 'utf-8'
if ( gradle.ext.javaVersions.test.launcher.asInt() >= 9 ) {
// See org.hibernate.boot.model.naming.NamingHelperTest.DefaultCharset.set
jvmArgs( ['--add-opens', 'java.base/java.nio.charset=ALL-UNNAMED'] )
// Weld needs this to generate proxies
jvmArgs( ['--add-opens', 'java.base/java.security=ALL-UNNAMED'] )
jvmArgs( ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] )
}
beforeTest { descriptor -> beforeTest { descriptor ->
//println "Starting test: " + descriptor //println "Starting test: " + descriptor
} }

View File

@ -201,23 +201,24 @@ public class ScanningCoordinator {
nonLocatedMappingFileNames.addAll( explicitMappingFileNames ); nonLocatedMappingFileNames.addAll( explicitMappingFileNames );
} }
for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) { if ( xmlMappingBinderAccess != null ) { // xml mapping is not disabled
managedResources.addXmlBinding( xmlMappingBinderAccess.bind( mappingFileDescriptor.getStreamAccess() ) ); for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) {
nonLocatedMappingFileNames.remove( mappingFileDescriptor.getName() ); managedResources.addXmlBinding( xmlMappingBinderAccess.bind( mappingFileDescriptor.getStreamAccess() ) );
} nonLocatedMappingFileNames.remove( mappingFileDescriptor.getName() );
for ( String name : nonLocatedMappingFileNames ) {
final URL url = classLoaderService.locateResource( name );
if ( url == null ) {
throw new MappingException(
"Unable to resolve explicitly named mapping-file : " + name,
new Origin( SourceType.RESOURCE, name )
);
} }
final UrlInputStreamAccess inputStreamAccess = new UrlInputStreamAccess( url );
managedResources.addXmlBinding( xmlMappingBinderAccess.bind( inputStreamAccess ) );
}
for ( String name : nonLocatedMappingFileNames ) {
final URL url = classLoaderService.locateResource( name );
if ( url == null ) {
throw new MappingException(
"Unable to resolve explicitly named mapping-file : " + name,
new Origin( SourceType.RESOURCE, name )
);
}
final UrlInputStreamAccess inputStreamAccess = new UrlInputStreamAccess( url );
managedResources.addXmlBinding( xmlMappingBinderAccess.bind( inputStreamAccess ) );
}
}
// classes and packages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // classes and packages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -289,28 +289,30 @@ public class MetadataBuildingProcess {
metadataCollector.processSecondPasses( rootMetadataBuildingContext ); metadataCollector.processSecondPasses( rootMetadataBuildingContext );
Iterable<AdditionalJaxbMappingProducer> producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class ); if ( options.isXmlMappingEnabled() ) {
if ( producers != null ) { Iterable<AdditionalJaxbMappingProducer> producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class );
final EntityHierarchyBuilder hierarchyBuilder = new EntityHierarchyBuilder(); if ( producers != null ) {
// final MappingBinder mappingBinder = new MappingBinder( true ); final EntityHierarchyBuilder hierarchyBuilder = new EntityHierarchyBuilder();
// We need to disable validation here. It seems Envers is not producing valid (according to schema) XML // final MappingBinder mappingBinder = new MappingBinder( true );
final MappingBinder mappingBinder = options.isXmlMappingEnabled() ? new MappingBinder( classLoaderService, false ) : null; // We need to disable validation here. It seems Envers is not producing valid (according to schema) XML
for ( AdditionalJaxbMappingProducer producer : producers ) { final MappingBinder mappingBinder = new MappingBinder( classLoaderService, false );
log.tracef( "Calling AdditionalJaxbMappingProducer : %s", producer ); for ( AdditionalJaxbMappingProducer producer : producers ) {
Collection<MappingDocument> additionalMappings = producer.produceAdditionalMappings( log.tracef( "Calling AdditionalJaxbMappingProducer : %s", producer );
metadataCollector, Collection<MappingDocument> additionalMappings = producer.produceAdditionalMappings(
jandexView, metadataCollector,
mappingBinder, jandexView,
rootMetadataBuildingContext mappingBinder,
); rootMetadataBuildingContext
for ( MappingDocument mappingDocument : additionalMappings ) { );
hierarchyBuilder.indexMappingDocument( mappingDocument ); for ( MappingDocument mappingDocument : additionalMappings ) {
hierarchyBuilder.indexMappingDocument( mappingDocument );
}
} }
}
ModelBinder binder = ModelBinder.prepare( rootMetadataBuildingContext ); ModelBinder binder = ModelBinder.prepare( rootMetadataBuildingContext );
for ( EntityHierarchySourceImpl entityHierarchySource : hierarchyBuilder.buildHierarchies() ) { for ( EntityHierarchySourceImpl entityHierarchySource : hierarchyBuilder.buildHierarchies() ) {
binder.bindEntityHierarchy( entityHierarchySource ); binder.bindEntityHierarchy( entityHierarchySource );
}
} }
} }

View File

@ -30,6 +30,7 @@ import org.hibernate.proxy.ProxyConfiguration;
import org.hibernate.proxy.ProxyFactory; import org.hibernate.proxy.ProxyFactory;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.TypeCache; import net.bytebuddy.TypeCache;
import net.bytebuddy.asm.AsmVisitorWrapper.ForDeclaredMethods; import net.bytebuddy.asm.AsmVisitorWrapper.ForDeclaredMethods;
import net.bytebuddy.asm.MemberSubstitution; import net.bytebuddy.asm.MemberSubstitution;
@ -71,7 +72,11 @@ public final class ByteBuddyState {
private final TypeCache<TypeCache.SimpleKey> basicProxyCache; private final TypeCache<TypeCache.SimpleKey> basicProxyCache;
ByteBuddyState() { ByteBuddyState() {
this.byteBuddy = new ByteBuddy().with( TypeValidation.DISABLED ); this( ClassFileVersion.ofThisVm( ClassFileVersion.JAVA_V8 ) );
}
ByteBuddyState(ClassFileVersion classFileVersion) {
this.byteBuddy = new ByteBuddy( classFileVersion ).with( TypeValidation.DISABLED );
this.proxyCache = new TypeCache.WithInlineExpunction<TypeCache.SimpleKey>( TypeCache.Sort.WEAK ); this.proxyCache = new TypeCache.WithInlineExpunction<TypeCache.SimpleKey>( TypeCache.Sort.WEAK );
this.basicProxyCache = new TypeCache.WithInlineExpunction<TypeCache.SimpleKey>( TypeCache.Sort.WEAK ); this.basicProxyCache = new TypeCache.WithInlineExpunction<TypeCache.SimpleKey>( TypeCache.Sort.WEAK );

View File

@ -20,6 +20,7 @@ import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.bytecode.spi.ReflectionOptimizer; import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper; import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.NamingStrategy; import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
@ -49,8 +50,21 @@ public class BytecodeProviderImpl implements BytecodeProvider {
private final ByteBuddyProxyHelper byteBuddyProxyHelper; private final ByteBuddyProxyHelper byteBuddyProxyHelper;
/**
* Constructs a ByteBuddy BytecodeProvider instance which attempts to auto-detect the target JVM version
* from the currently running one, with a fallback on Java 8.
*/
public BytecodeProviderImpl() { public BytecodeProviderImpl() {
this.byteBuddyState = new ByteBuddyState(); this( ClassFileVersion.ofThisVm( ClassFileVersion.JAVA_V8 ) );
}
/**
* Constructs a ByteBuddy BytecodeProvider instance which aims to produce code compatible
* with the specified target JVM version.
* @param targetCompatibleJVM
*/
public BytecodeProviderImpl(ClassFileVersion targetCompatibleJVM) {
this.byteBuddyState = new ByteBuddyState( targetCompatibleJVM );
this.byteBuddyProxyHelper = new ByteBuddyProxyHelper( byteBuddyState ); this.byteBuddyProxyHelper = new ByteBuddyProxyHelper( byteBuddyState );
} }

View File

@ -51,6 +51,7 @@ import org.hibernate.annotations.LazyGroup;
import org.hibernate.annotations.Loader; import org.hibernate.annotations.Loader;
import org.hibernate.annotations.ManyToAny; import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.OptimisticLock; import org.hibernate.annotations.OptimisticLock;
import org.hibernate.annotations.OrderBy; import org.hibernate.annotations.OrderBy;
import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Parameter;
@ -104,6 +105,7 @@ import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
@ -557,7 +559,9 @@ public abstract class CollectionBinder {
} }
//TODO reduce tableBinder != null and oneToMany //TODO reduce tableBinder != null and oneToMany
XClass collectionType = getCollectionType(); XClass collectionType = getCollectionType();
if ( inheritanceStatePerClass == null) throw new AssertionFailure( "inheritanceStatePerClass not set" ); if ( inheritanceStatePerClass == null) {
throw new AssertionFailure( "inheritanceStatePerClass not set" );
}
SecondPass sp = getSecondPass( SecondPass sp = getSecondPass(
fkJoinColumns, fkJoinColumns,
joinColumns, joinColumns,
@ -606,7 +610,9 @@ public abstract class CollectionBinder {
binder.setUpdatable( updatable ); binder.setUpdatable( updatable );
Property prop = binder.makeProperty(); Property prop = binder.makeProperty();
//we don't care about the join stuffs because the column is on the association table. //we don't care about the join stuffs because the column is on the association table.
if (! declaringClassSet) throw new AssertionFailure( "DeclaringClass is not set in CollectionBinder while binding" ); if (! declaringClassSet) {
throw new AssertionFailure( "DeclaringClass is not set in CollectionBinder while binding" );
}
propertyHolder.addProperty( prop, declaringClass ); propertyHolder.addProperty( prop, declaringClass );
} }
@ -614,7 +620,7 @@ public abstract class CollectionBinder {
boolean hadOrderBy = false; boolean hadOrderBy = false;
boolean hadExplicitSort = false; boolean hadExplicitSort = false;
Class<? extends Comparator> comparatorClass = null; Class<? extends Comparator<?>> comparatorClass = null;
if ( jpaOrderBy == null && sqlOrderBy == null ) { if ( jpaOrderBy == null && sqlOrderBy == null ) {
if ( deprecatedSort != null ) { if ( deprecatedSort != null ) {
@ -779,8 +785,9 @@ public abstract class CollectionBinder {
final TableBinder assocTableBinder, final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) { final MetadataBuildingContext buildingContext) {
return new CollectionSecondPass( buildingContext, collection ) { return new CollectionSecondPass( buildingContext, collection ) {
@SuppressWarnings("rawtypes")
@Override @Override
public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas) throws MappingException { public void secondPass(Map persistentClasses, Map inheritedMetas) throws MappingException {
bindStarToManySecondPass( bindStarToManySecondPass(
persistentClasses, persistentClasses,
collType, collType,
@ -803,7 +810,7 @@ public abstract class CollectionBinder {
* return true if it's a Fk, false if it's an association table * return true if it's a Fk, false if it's an association table
*/ */
protected boolean bindStarToManySecondPass( protected boolean bindStarToManySecondPass(
Map persistentClasses, Map<String, PersistentClass> persistentClasses,
XClass collType, XClass collType,
Ejb3JoinColumn[] fkJoinColumns, Ejb3JoinColumn[] fkJoinColumns,
Ejb3JoinColumn[] keyColumns, Ejb3JoinColumn[] keyColumns,
@ -815,7 +822,7 @@ public abstract class CollectionBinder {
TableBinder associationTableBinder, TableBinder associationTableBinder,
boolean ignoreNotFound, boolean ignoreNotFound,
MetadataBuildingContext buildingContext) { MetadataBuildingContext buildingContext) {
PersistentClass persistentClass = (PersistentClass) persistentClasses.get( collType.getName() ); PersistentClass persistentClass = persistentClasses.get( collType.getName() );
boolean reversePropertyInJoin = false; boolean reversePropertyInJoin = false;
if ( persistentClass != null && StringHelper.isNotEmpty( this.mappedBy ) ) { if ( persistentClass != null && StringHelper.isNotEmpty( this.mappedBy ) ) {
try { try {
@ -876,7 +883,7 @@ public abstract class CollectionBinder {
protected void bindOneToManySecondPass( protected void bindOneToManySecondPass(
Collection collection, Collection collection,
Map persistentClasses, Map<String, PersistentClass> persistentClasses,
Ejb3JoinColumn[] fkJoinColumns, Ejb3JoinColumn[] fkJoinColumns,
XClass collectionType, XClass collectionType,
boolean cascadeDeleteEnabled, boolean cascadeDeleteEnabled,
@ -898,7 +905,7 @@ public abstract class CollectionBinder {
oneToMany.setIgnoreNotFound( ignoreNotFound ); oneToMany.setIgnoreNotFound( ignoreNotFound );
String assocClass = oneToMany.getReferencedEntityName(); String assocClass = oneToMany.getReferencedEntityName();
PersistentClass associatedClass = (PersistentClass) persistentClasses.get( assocClass ); PersistentClass associatedClass = persistentClasses.get( assocClass );
if ( jpaOrderBy != null ) { if ( jpaOrderBy != null ) {
final String orderByFragment = buildOrderByClauseFromHql( final String orderByFragment = buildOrderByClauseFromHql(
jpaOrderBy.value(), jpaOrderBy.value(),
@ -1215,8 +1222,8 @@ public abstract class CollectionBinder {
key.setForeignKeyName( StringHelper.nullIfEmpty( collectionTableAnn.foreignKey().name() ) ); key.setForeignKeyName( StringHelper.nullIfEmpty( collectionTableAnn.foreignKey().name() ) );
key.setForeignKeyDefinition( StringHelper.nullIfEmpty( collectionTableAnn.foreignKey().foreignKeyDefinition() ) ); key.setForeignKeyDefinition( StringHelper.nullIfEmpty( collectionTableAnn.foreignKey().foreignKeyDefinition() ) );
if ( key.getForeignKeyName() == null && if ( key.getForeignKeyName() == null &&
key.getForeignKeyDefinition() == null && key.getForeignKeyDefinition() == null &&
collectionTableAnn.joinColumns().length == 1 ) { collectionTableAnn.joinColumns().length == 1 ) {
JoinColumn joinColumn = collectionTableAnn.joinColumns()[0]; JoinColumn joinColumn = collectionTableAnn.joinColumns()[0];
key.setForeignKeyName( StringHelper.nullIfEmpty( joinColumn.foreignKey().name() ) ); key.setForeignKeyName( StringHelper.nullIfEmpty( joinColumn.foreignKey().name() ) );
key.setForeignKeyDefinition( StringHelper.nullIfEmpty( joinColumn.foreignKey().foreignKeyDefinition() ) ); key.setForeignKeyDefinition( StringHelper.nullIfEmpty( joinColumn.foreignKey().foreignKeyDefinition() ) );
@ -1261,15 +1268,25 @@ public abstract class CollectionBinder {
key.setForeignKeyDefinition( StringHelper.nullIfEmpty( fkOverride.foreignKeyDefinition() ) ); key.setForeignKeyDefinition( StringHelper.nullIfEmpty( fkOverride.foreignKeyDefinition() ) );
} }
else { else {
final JoinColumn joinColumnAnn = property.getAnnotation( JoinColumn.class ); final OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
if ( joinColumnAnn != null ) { final OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
if ( joinColumnAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT if ( oneToManyAnn != null && !oneToManyAnn.mappedBy().isEmpty()
|| joinColumnAnn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) { && ( onDeleteAnn == null || onDeleteAnn.action() != OnDeleteAction.CASCADE ) ) {
key.setForeignKeyName( "none" ); // foreign key should be up to @ManyToOne side
} // @OnDelete generate "on delete cascade" foreign key
else { key.setForeignKeyName( "none" );
key.setForeignKeyName( StringHelper.nullIfEmpty( joinColumnAnn.foreignKey().name() ) ); }
key.setForeignKeyDefinition( StringHelper.nullIfEmpty( joinColumnAnn.foreignKey().foreignKeyDefinition() ) ); else {
final JoinColumn joinColumnAnn = property.getAnnotation( JoinColumn.class );
if ( joinColumnAnn != null ) {
if ( joinColumnAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|| joinColumnAnn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
key.setForeignKeyName( "none" );
}
else {
key.setForeignKeyName( StringHelper.nullIfEmpty( joinColumnAnn.foreignKey().name() ) );
key.setForeignKeyDefinition( StringHelper.nullIfEmpty( joinColumnAnn.foreignKey().foreignKeyDefinition() ) );
}
} }
} }
} }
@ -1283,7 +1300,7 @@ public abstract class CollectionBinder {
private void bindManyToManySecondPass( private void bindManyToManySecondPass(
Collection collValue, Collection collValue,
Map persistentClasses, Map<String, PersistentClass> persistentClasses,
Ejb3JoinColumn[] joinColumns, Ejb3JoinColumn[] joinColumns,
Ejb3JoinColumn[] inverseJoinColumns, Ejb3JoinColumn[] inverseJoinColumns,
Ejb3Column[] elementColumns, Ejb3Column[] elementColumns,
@ -1299,7 +1316,7 @@ public abstract class CollectionBinder {
throw new IllegalArgumentException( "null was passed for argument property" ); throw new IllegalArgumentException( "null was passed for argument property" );
} }
final PersistentClass collectionEntity = (PersistentClass) persistentClasses.get( collType.getName() ); final PersistentClass collectionEntity = persistentClasses.get( collType.getName() );
final String hqlOrderBy = extractHqlOrderBy( jpaOrderBy ); final String hqlOrderBy = extractHqlOrderBy( jpaOrderBy );
boolean isCollectionOfEntities = collectionEntity != null; boolean isCollectionOfEntities = collectionEntity != null;
@ -1732,13 +1749,13 @@ public abstract class CollectionBinder {
final String mappedBy = columns[0].getMappedBy(); final String mappedBy = columns[0].getMappedBy();
if ( StringHelper.isNotEmpty( mappedBy ) ) { if ( StringHelper.isNotEmpty( mappedBy ) ) {
final Property property = referencedEntity.getRecursiveProperty( mappedBy ); final Property property = referencedEntity.getRecursiveProperty( mappedBy );
Iterator mappedByColumns; Iterator<Selectable> mappedByColumns;
if ( property.getValue() instanceof Collection ) { if ( property.getValue() instanceof Collection ) {
mappedByColumns = ( (Collection) property.getValue() ).getKey().getColumnIterator(); mappedByColumns = ( (Collection) property.getValue() ).getKey().getColumnIterator();
} }
else { else {
//find the appropriate reference key, can be in a join //find the appropriate reference key, can be in a join
Iterator joinsIt = referencedEntity.getJoinIterator(); Iterator<Join> joinsIt = referencedEntity.getJoinIterator();
KeyValue key = null; KeyValue key = null;
while ( joinsIt.hasNext() ) { while ( joinsIt.hasNext() ) {
Join join = (Join) joinsIt.next(); Join join = (Join) joinsIt.next();
@ -1747,7 +1764,9 @@ public abstract class CollectionBinder {
break; break;
} }
} }
if ( key == null ) key = property.getPersistentClass().getIdentifier(); if ( key == null ) {
key = property.getPersistentClass().getIdentifier();
}
mappedByColumns = key.getColumnIterator(); mappedByColumns = key.getColumnIterator();
} }
while ( mappedByColumns.hasNext() ) { while ( mappedByColumns.hasNext() ) {

View File

@ -788,6 +788,13 @@ public class PostgreSQLDialect extends Dialect {
super.augmentRecognizedTableTypes( tableTypesList ); super.augmentRecognizedTableTypes( tableTypesList );
if ( getVersion() >= 930 ) { if ( getVersion() >= 930 ) {
tableTypesList.add( "MATERIALIZED VIEW" ); tableTypesList.add( "MATERIALIZED VIEW" );
/*
PostgreSQL 10 and later adds support for Partition table.
*/
if ( getVersion() >= 1000 ) {
tableTypesList.add( "PARTITIONED TABLE" );
}
} }
} }

View File

@ -2540,7 +2540,8 @@ public abstract class AbstractEntityPersister
mutablePropertiesIndexes.stream().forEach( i -> { mutablePropertiesIndexes.stream().forEach( i -> {
// This is kindly borrowed from org.hibernate.type.TypeHelper.findDirty // This is kindly borrowed from org.hibernate.type.TypeHelper.findDirty
final boolean dirty = currentState[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY && final boolean dirty = currentState[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY &&
( previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || // Consider mutable properties as dirty if we don't have a previous state
( previousState == null || previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ||
( propertyCheckability[i] ( propertyCheckability[i]
&& propertyTypes[i].isDirty( && propertyTypes[i].isDirty(
previousState[i], previousState[i],

View File

@ -142,9 +142,8 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
final PersistenceContext persistenceContext = session.getPersistenceContext(); final PersistenceContext persistenceContext = session.getPersistenceContext();
// first, instantiate the entity instance to use as the proxy // first, instantiate the entity instance to use as the proxy
final EntityTuplizer entityTuplizer = persister.getEntityTuplizer(); final PersistentAttributeInterceptable entity = (PersistentAttributeInterceptable) persister.getRepresentationStrategy().getInstantiator().instantiate(
final PersistentAttributeInterceptable entity = (PersistentAttributeInterceptable) entityTuplizer session.getFactory() );
.instantiate( identifier, session );
// clear the fields that are marked as dirty in the dirtiness tracker // clear the fields that are marked as dirty in the dirtiness tracker
if ( entity instanceof SelfDirtinessTracker ) { if ( entity instanceof SelfDirtinessTracker ) {

View File

@ -0,0 +1,193 @@
/*
* 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.test.annotations.derivedidentities.bidirectional;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
public class OneToOneEagerDerivedIdFetchModeSelectTest extends BaseCoreFunctionalTestCase {
private Foo foo;
@Test
@TestForIssue(jiraKey = "HHH-14390")
@FailureExpected(jiraKey = "HHH-14390")
public void testQuery() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo.id = :id" )
.setParameter( "id", foo.getId() )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
@FailureExpected(jiraKey = "HHH-14390")
public void testQueryById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo = :foo" )
.setParameter( "foo", foo )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@FailureExpected( jiraKey = "HHH-14389")
public void testFindById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertSame( foo, newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
@FailureExpected(jiraKey = "HHH-14390")
public void testFindByPrimaryKey() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo.getId() );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Before
public void setupData() {
this.foo = doInHibernate( this::sessionFactory, session -> {
Foo foo = new Foo();
session.persist( foo );
Bar bar = new Bar();
bar.setFoo( foo );
bar.setDetails( "Some details" );
foo.setBar( bar );
session.persist( bar );
session.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
return foo;
});
}
@After
public void cleanupData() {
this.foo = null;
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Bar" );
session.createQuery( "delete from Foo" );
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Foo.class,
Bar.class,
};
}
@Entity(name = "Foo")
public static class Foo implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne(mappedBy = "foo", cascade = CascadeType.ALL)
private Bar bar;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
@Entity(name = "Bar")
public static class Bar implements Serializable {
@Id
@OneToOne(fetch = FetchType.EAGER)
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "BAR_ID")
private Foo foo;
private String details;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
}

View File

@ -0,0 +1,189 @@
/*
* 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.test.annotations.derivedidentities.bidirectional;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
public class OneToOneLazyDerivedIdFetchModeSelectTest extends BaseCoreFunctionalTestCase {
private Foo foo;
@Test
@TestForIssue( jiraKey = "HHH-14390")
public void testQuery() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo.id = :id" )
.setParameter( "id", foo.getId() )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
public void testQueryById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo = :foo" )
.setParameter( "foo", foo )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@FailureExpected( jiraKey = "HHH-14389")
public void testFindById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertSame( foo, newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
public void testFindByPrimaryKey() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo.getId() );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Before
public void setupData() {
this.foo = doInHibernate( this::sessionFactory, session -> {
Foo foo = new Foo();
session.persist( foo );
Bar bar = new Bar();
bar.setFoo( foo );
bar.setDetails( "Some details" );
foo.setBar( bar );
session.persist( bar );
session.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
return foo;
});
}
@After
public void cleanupData() {
this.foo = null;
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Bar" );
session.createQuery( "delete from Foo" );
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Foo.class,
Bar.class,
};
}
@Entity(name = "Foo")
public static class Foo implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne(mappedBy = "foo", cascade = CascadeType.ALL)
private Bar bar;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
@Entity(name = "Bar")
public static class Bar implements Serializable {
@Id
@OneToOne(fetch = FetchType.LAZY)
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "BAR_ID")
private Foo foo;
private String details;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
}

View File

@ -51,6 +51,85 @@ public class OneToOneWithDerivedIdentityTest extends BaseCoreFunctionalTestCase
s.close(); s.close();
} }
@Test
@TestForIssue(jiraKey = "HHH-14389")
public void testQueryById() {
Session s = openSession();
s.beginTransaction();
Bar bar = new Bar();
bar.setDetails( "Some details" );
Foo foo = new Foo();
foo.setBar( bar );
bar.setFoo( foo );
s.persist( foo );
s.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
s.clear();
Bar newBar = ( Bar ) s.createQuery( "SELECT b FROM Bar b WHERE b.foo = :foo" )
.setParameter( "foo", foo )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
s.getTransaction().rollback();
s.close();
}
@Test
@TestForIssue(jiraKey = "HHH-14389")
@FailureExpected( jiraKey = "HHH-14389")
public void testFindById() {
Session s = openSession();
s.beginTransaction();
Bar bar = new Bar();
bar.setDetails( "Some details" );
Foo foo = new Foo();
foo.setBar( bar );
bar.setFoo( foo );
s.persist( foo );
s.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
s.clear();
Bar newBar = s.find( Bar.class, foo );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertSame( foo, newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
s.getTransaction().rollback();
s.close();
}
@Test
@TestForIssue(jiraKey = "HHH-14389")
public void testFindByPrimaryKey() {
Session s = openSession();
s.beginTransaction();
Bar bar = new Bar();
bar.setDetails( "Some details" );
Foo foo = new Foo();
foo.setBar( bar );
bar.setFoo( foo );
s.persist( foo );
s.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
s.clear();
Bar newBar = s.find( Bar.class, foo.getId() );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
s.getTransaction().rollback();
s.close();
}
@Test @Test
@TestForIssue( jiraKey = "HHH-10476") @TestForIssue( jiraKey = "HHH-10476")
public void testInsertFooAndBarWithDerivedIdPC() { public void testInsertFooAndBarWithDerivedIdPC() {

View File

@ -0,0 +1,173 @@
/*
* 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.test.annotations.derivedidentities.unidirectional;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
public class OneToOneEagerDerivedIdFetchModeSelectTest extends BaseCoreFunctionalTestCase {
private Foo foo;
@Test
@TestForIssue( jiraKey = "HHH-14390")
public void testQuery() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo.id = :id" )
.setParameter( "id", foo.getId() )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
public void testQueryById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo = :foo" )
.setParameter( "foo", foo )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@FailureExpected( jiraKey = "HHH-14389")
public void testFindById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertSame( foo, newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
public void testFindByPrimaryKey() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo.getId() );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Before
public void setupData() {
this.foo = doInHibernate( this::sessionFactory, session -> {
Foo foo = new Foo();
session.persist( foo );
Bar bar = new Bar();
bar.setFoo( foo );
bar.setDetails( "Some details" );
session.persist( bar );
session.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
return foo;
});
}
@After
public void cleanupData() {
this.foo = null;
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Bar" );
session.createQuery( "delete from Foo" );
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Foo.class,
Bar.class,
};
}
@Entity(name = "Foo")
public static class Foo implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
@Entity(name = "Bar")
public static class Bar implements Serializable {
@Id
@OneToOne(fetch = FetchType.EAGER)
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "BAR_ID")
private Foo foo;
private String details;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
}

View File

@ -0,0 +1,174 @@
/*
* 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.test.annotations.derivedidentities.unidirectional;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
public class OneToOneLazyDerivedIdFetchModeSelectTest extends BaseCoreFunctionalTestCase {
private Foo foo;
@Test
@TestForIssue( jiraKey = "HHH-14390")
public void testQuery() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo.id = :id" )
.setParameter( "id", foo.getId() )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
public void testQueryById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo = :foo" )
.setParameter( "foo", foo )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@FailureExpected( jiraKey = "HHH-14389")
public void testFindById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertSame( foo, newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14390")
public void testFindByPrimaryKey() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo.getId() );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Before
public void setupData() {
this.foo = doInHibernate( this::sessionFactory, session -> {
Foo foo = new Foo();
session.persist( foo );
Bar bar = new Bar();
bar.setFoo( foo );
bar.setDetails( "Some details" );
session.persist( bar );
session.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
return foo;
});
}
@After
public void cleanupData() {
this.foo = null;
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Bar" );
session.createQuery( "delete from Foo" );
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Foo.class,
Bar.class,
};
}
@Entity(name = "Foo")
public static class Foo implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
@Entity(name = "Bar")
public static class Bar implements Serializable {
@Id
@OneToOne(fetch = FetchType.LAZY)
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "BAR_ID")
private Foo foo;
private String details;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.test.annotations.selectbeforeupdate;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.persistence.Column;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.persistence.Entity; import javax.persistence.Entity;
@ -280,6 +281,7 @@ public class UpdateDetachedTest extends BaseCoreFunctionalTestCase{
@Embeddable @Embeddable
public static class Comment { public static class Comment {
@Column(name = "bar_comment")
private String comment; private String comment;
private String author; private String author;

View File

@ -0,0 +1,182 @@
/*
* 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.test.bytecode.enhancement.dirty;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.OrderColumn;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.boot.internal.SessionFactoryBuilderImpl;
import org.hibernate.boot.internal.SessionFactoryOptionsBuilder;
import org.hibernate.boot.spi.SessionFactoryBuilderService;
import org.hibernate.cfg.Configuration;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Christian Beikov
*/
@TestForIssue(jiraKey = "HHH-14360")
@RunWith(BytecodeEnhancerRunner.class)
public class DirtyTrackingPersistTest extends BaseCoreFunctionalTestCase {
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { HotherEntity.class, Hentity.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getStandardServiceRegistryBuilder().addService(
SessionFactoryBuilderService.class,
(SessionFactoryBuilderService) (metadata, bootstrapContext) -> {
SessionFactoryOptionsBuilder optionsBuilder = new SessionFactoryOptionsBuilder(
metadata.getMetadataBuildingOptions().getServiceRegistry(),
bootstrapContext
);
optionsBuilder.enableCollectionInDefaultFetchGroup( true );
return new SessionFactoryBuilderImpl( metadata, optionsBuilder );
}
);
}
@Test
public void test() {
Hentity hentity = new Hentity();
HotherEntity hotherEntity = new HotherEntity();
hentity.setLineItems( new ArrayList<>( Collections.singletonList( hotherEntity ) ) );
hentity.setNextRevUNs( new ArrayList<>( Collections.singletonList( "something" ) ) );
doInHibernate( this::sessionFactory, session -> {
session.persist( hentity );
} );
doInHibernate( this::sessionFactory, session -> {
hentity.bumpNumber();
session.saveOrUpdate( hentity );
} );
}
// --- //
@Entity(name = "HotherEntity")
public static class HotherEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Basic
private Long clicId;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public Long getClicId() {
return clicId;
}
public void setClicId(Long clicId) {
this.clicId = clicId;
}
}
@Entity(name = "Hentity")
public static class Hentity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ElementCollection
@OrderColumn(name = "nextRevUN_index")
private List<String> nextRevUNs;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "clicId")
@OrderBy("id asc")
protected List<HotherEntity> lineItems;
@Basic
private Long aNumber;
@Temporal(value = TemporalType.TIMESTAMP)
private Date createDate;
@Temporal(value = TemporalType.TIMESTAMP)
private Date deleteDate;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public List<String> getNextRevUNs() {
return nextRevUNs;
}
public void setNextRevUNs(List<String> nextRevUNs) {
this.nextRevUNs = nextRevUNs;
}
public List<HotherEntity> getLineItems() {
return lineItems;
}
public void setLineItems(List<HotherEntity> lineItems) {
this.lineItems = lineItems;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getDeleteDate() {
return deleteDate;
}
public void setDeleteDate(Date deleteDate) {
this.deleteDate = deleteDate;
}
public void bumpNumber() {
aNumber = aNumber == null ? 0 : aNumber++;
}
}
}

View File

@ -40,29 +40,24 @@ public abstract class AbstractKey extends ModelEntity
String name; String name;
@OneToMany(targetEntity = RoleEntity.class, mappedBy = "key", fetch = FetchType.LAZY) @OneToMany(targetEntity = RoleEntity.class, mappedBy = "key", fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("R") @LazyGroup("R")
protected Set<RoleEntity> roles = new LinkedHashSet<>(); protected Set<RoleEntity> roles = new LinkedHashSet<>();
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("PR") @LazyGroup("PR")
@JoinColumn @JoinColumn
protected AbstractKey register; protected AbstractKey register;
@OneToMany(targetEntity = AbstractKey.class, mappedBy = "register", fetch = FetchType.LAZY) @OneToMany(targetEntity = AbstractKey.class, mappedBy = "register", fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("RK") @LazyGroup("RK")
protected Set<AbstractKey> keys = new LinkedHashSet(); protected Set<AbstractKey> keys = new LinkedHashSet();
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("PP") @LazyGroup("PP")
@JoinColumn @JoinColumn
protected AbstractKey parent; protected AbstractKey parent;
@OneToMany(targetEntity = AbstractKey.class, mappedBy = "parent", fetch = FetchType.LAZY) @OneToMany(targetEntity = AbstractKey.class, mappedBy = "parent", fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("PK") @LazyGroup("PK")
protected Set<AbstractKey> otherKeys = new LinkedHashSet(); protected Set<AbstractKey> otherKeys = new LinkedHashSet();

View File

@ -52,7 +52,6 @@ public class Activity extends BaseEntity {
} }
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("Instruction") @LazyGroup("Instruction")
@JoinColumn(name = "Instruction_Id") @JoinColumn(name = "Instruction_Id")
public Instruction getInstruction() { public Instruction getInstruction() {
@ -66,7 +65,6 @@ public class Activity extends BaseEntity {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ManyToOne(fetch=FetchType.LAZY) @ManyToOne(fetch=FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("webApplication") @LazyGroup("webApplication")
@JoinColumn(name="web_app_oid") @JoinColumn(name="web_app_oid")
public WebApplication getWebApplication() { public WebApplication getWebApplication() {

View File

@ -246,7 +246,6 @@ public class BatchFetchProxyTest extends BaseNonConfigCoreFunctionalTestCase {
private String name; private String name;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Employer employer; private Employer employer;
} }

View File

@ -211,7 +211,6 @@ public class BidirectionalProxyTest extends BaseNonConfigCoreFunctionalTestCase
} }
@ManyToOne(fetch= FetchType.LAZY) @ManyToOne(fetch= FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("aEntity") @LazyGroup("aEntity")
@JoinColumn(name="aEntity") @JoinColumn(name="aEntity")
protected AEntity aChildEntity = null; protected AEntity aChildEntity = null;
@ -249,7 +248,6 @@ public class BidirectionalProxyTest extends BaseNonConfigCoreFunctionalTestCase
} }
@ManyToOne(fetch= FetchType.LAZY) @ManyToOne(fetch= FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("aEntity") @LazyGroup("aEntity")
@JoinColumn(name="aEntity") @JoinColumn(name="aEntity")
protected AChildEntity aChildEntity = null; protected AChildEntity aChildEntity = null;

View File

@ -212,7 +212,6 @@ public class EntitySharedInCollectionAndToOneTest extends BaseNonConfigCoreFunct
private CodeTable codeTable; private CodeTable codeTable;
@OneToOne( mappedBy = "defaultItem", fetch=FetchType.LAZY ) @OneToOne( mappedBy = "defaultItem", fetch=FetchType.LAZY )
@LazyToOne( LazyToOneOption.NO_PROXY )
@LazyGroup( "defaultItemInverse" ) @LazyGroup( "defaultItemInverse" )
protected CodeTable defaultItemInverse; protected CodeTable defaultItemInverse;

View File

@ -38,6 +38,7 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
@ -199,6 +200,7 @@ public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase {
} }
@Test @Test
@FailureExpected( jiraKey = "HHH-13658")
public void testRandomAccess() { public void testRandomAccess() {
final StatisticsImplementor stats = sessionFactory().getStatistics(); final StatisticsImplementor stats = sessionFactory().getStatistics();
stats.clear(); stats.clear();
@ -787,14 +789,10 @@ public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase {
// ****** Relations ***************** // ****** Relations *****************
@OneToOne(fetch = FetchType.LAZY) @OneToOne(fetch = FetchType.LAZY)
// @LazyToOne(LazyToOneOption.PROXY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("a") @LazyGroup("a")
public AEntity a; public AEntity a;
@OneToOne(fetch = FetchType.LAZY) @OneToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
// @LazyToOne(LazyToOneOption.PROXY)
@LazyGroup("c") @LazyGroup("c")
public CEntity c; public CEntity c;
@ -802,7 +800,6 @@ public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase {
public Set<BEntity> bs; public Set<BEntity> bs;
@OneToOne(mappedBy = "d", fetch = FetchType.LAZY) @OneToOne(mappedBy = "d", fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("e") @LazyGroup("e")
private EEntity e; private EEntity e;

View File

@ -264,19 +264,15 @@ public class LazyToOnesNoProxyFactoryWithSubclassesStatefulTest extends BaseNonC
private String id; private String id;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Animal animal = null; private Animal animal = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Primate primate = null; private Primate primate = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Human human = null; private Human human = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Human otherHuman = null; private Human otherHuman = null;
protected OtherEntity() { protected OtherEntity() {

View File

@ -264,19 +264,15 @@ public class LazyToOnesNoProxyFactoryWithSubclassesStatelessTest extends BaseNon
private String id; private String id;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Animal animal = null; private Animal animal = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Primate primate = null; private Primate primate = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Human human = null; private Human human = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Human otherHuman = null; private Human otherHuman = null;
protected OtherEntity() { protected OtherEntity() {

View File

@ -831,15 +831,12 @@ public class LazyToOnesProxyMergeWithSubclassesTest extends BaseNonConfigCoreFun
private String id; private String id;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Animal animal = null; private Animal animal = null;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Primate primate = null; private Primate primate = null;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Human human = null; private Human human = null;
protected OtherEntity() { protected OtherEntity() {

View File

@ -298,15 +298,12 @@ public class LazyToOnesProxyWithSubclassesStatelessTest extends BaseNonConfigCor
private String id; private String id;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Animal animal = null; private Animal animal = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Primate primate = null; private Primate primate = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Human human = null; private Human human = null;
protected OtherEntity() { protected OtherEntity() {

View File

@ -366,15 +366,12 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction
private String id; private String id;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Animal animal = null; private Animal animal = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Primate primate = null; private Primate primate = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Human human = null; private Human human = null;
protected OtherEntity() { protected OtherEntity() {

View File

@ -180,7 +180,6 @@ public class LoadANonExistingEntityTest extends BaseNonConfigCoreFunctionalTestC
private String name; private String name;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Employer employer; private Employer employer;
} }

View File

@ -203,7 +203,6 @@ public class LoadANonExistingNotFoundBatchEntityTest extends BaseNonConfigCoreFu
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(foreignKey = @ForeignKey(value= ConstraintMode.NO_CONSTRAINT)) @JoinColumn(foreignKey = @ForeignKey(value= ConstraintMode.NO_CONSTRAINT))
@LazyToOne(LazyToOneOption.NO_PROXY)
@NotFound(action=NotFoundAction.IGNORE) @NotFound(action=NotFoundAction.IGNORE)
private Employer employer; private Employer employer;
} }

View File

@ -183,7 +183,6 @@ public class LoadANonExistingNotFoundEntityTest extends BaseNonConfigCoreFunctio
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(foreignKey = @ForeignKey(value= ConstraintMode.NO_CONSTRAINT)) @JoinColumn(foreignKey = @ForeignKey(value= ConstraintMode.NO_CONSTRAINT))
@LazyToOne(LazyToOneOption.NO_PROXY)
@NotFound(action=NotFoundAction.IGNORE) @NotFound(action=NotFoundAction.IGNORE)
private Employer employer; private Employer employer;
} }

View File

@ -153,7 +153,6 @@ public class MapsIdProxyBidirectionalTest extends BaseNonConfigCoreFunctionalTes
@MapsId @MapsId
@OneToOne(optional = false, fetch = FetchType.LAZY) @OneToOne(optional = false, fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Employer employer; private Employer employer;
private String info; private String info;
@ -165,7 +164,6 @@ public class MapsIdProxyBidirectionalTest extends BaseNonConfigCoreFunctionalTes
private int id; private int id;
@OneToOne(optional = false, fetch = FetchType.LAZY, mappedBy = "employer", cascade = CascadeType.ALL) @OneToOne(optional = false, fetch = FetchType.LAZY, mappedBy = "employer", cascade = CascadeType.ALL)
@LazyToOne(LazyToOneOption.NO_PROXY)
private EmployerInfo employerInfo; private EmployerInfo employerInfo;
private String name; private String name;

View File

@ -122,7 +122,6 @@ public class MapsIdProxyUnidirectionalTest extends BaseNonConfigCoreFunctionalTe
@MapsId @MapsId
@OneToOne(optional = false, fetch = FetchType.LAZY) @OneToOne(optional = false, fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Employer employer; private Employer employer;
} }

View File

@ -177,7 +177,6 @@ public class MergeDetachedToProxyTest extends BaseNonConfigCoreFunctionalTestCas
private int id; private int id;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private BEntity bEntity; private BEntity bEntity;
} }

View File

@ -302,7 +302,6 @@ public class QueryScrollingWithInheritanceProxyEagerManyToOneTest extends BaseNo
private String id; private String id;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@JoinColumn(name = "Employee_Id") @JoinColumn(name = "Employee_Id")
protected Employee employee = null; protected Employee employee = null;

View File

@ -302,12 +302,10 @@ public class QueryScrollingWithInheritanceProxyTest extends BaseNonConfigCoreFun
private String id; private String id;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@JoinColumn(name = "Employee_Id") @JoinColumn(name = "Employee_Id")
protected Employee employee = null; protected Employee employee = null;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@JoinColumn(name = "EmployeeParent_Id") @JoinColumn(name = "EmployeeParent_Id")
protected EmployeeParent employeeParent = null; protected EmployeeParent employeeParent = null;

View File

@ -283,7 +283,6 @@ public class SimpleUpdateTestWithLazyLoading extends BaseNonConfigCoreFunctional
Long id; Long id;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
Parent parent; Parent parent;
@OneToMany @OneToMany

View File

@ -235,7 +235,6 @@ public class SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking extends BaseN
Long id; Long id;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
Parent parent; Parent parent;
@OneToMany @OneToMany

View File

@ -59,7 +59,6 @@ public class WebApplication extends BaseEntity {
} }
@OneToMany(mappedBy="webApplication", fetch= FetchType.LAZY) @OneToMany(mappedBy="webApplication", fetch= FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("app_activity_group") @LazyGroup("app_activity_group")
// @CollectionType(type="baseutil.technology.hibernate.IskvLinkedSetCollectionType") // @CollectionType(type="baseutil.technology.hibernate.IskvLinkedSetCollectionType")
public Set<Activity> getActivities() { public Set<Activity> getActivities() {

View File

@ -23,6 +23,7 @@ public class TestEntity {
private Long id; private Long id;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
@Column(name = "t_date")
private Date date; private Date date;
@Basic @Basic

View File

@ -0,0 +1,94 @@
/*
* 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.test.foreignkeys.disabled;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.StreamSupport;
import javax.persistence.CascadeType;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.mapping.Table;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
/**
* {@inheritDoc}
*
* @author Yanming Zhou
*/
@TestForIssue(jiraKey = "HHH-14229")
public class OneToManyBidirectionalForeignKeyTest {
private static final String TABLE_NAME_PLAIN = "plain";
private static final String TABLE_NAME_WITH_ON_DELETE = "cascade_delete";
@Test
public void testForeignKeyShouldNotBeCreated() {
Metadata metadata = new MetadataSources(new StandardServiceRegistryBuilder().build())
.addAnnotatedClass(PlainTreeEntity.class).addAnnotatedClass(TreeEntityWithOnDelete.class)
.buildMetadata();
assertTrue(findTable(metadata, TABLE_NAME_PLAIN).getForeignKeys().isEmpty());
assertFalse(findTable(metadata, TABLE_NAME_WITH_ON_DELETE).getForeignKeys().isEmpty());
}
private static Table findTable(Metadata metadata, String tableName) {
return StreamSupport.stream(metadata.getDatabase().getNamespaces().spliterator(), false)
.flatMap(namespace -> namespace.getTables().stream()).filter(t -> t.getName().equals(tableName))
.findFirst().orElse(null);
}
@Entity
@javax.persistence.Table(name = TABLE_NAME_PLAIN)
public static class PlainTreeEntity {
@Id
private Long id;
@ManyToOne
@JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private PlainTreeEntity parent;
@OneToMany(mappedBy = "parent")
// workaround
// @org.hibernate.annotations.ForeignKey(name = "none")
private Collection<PlainTreeEntity> children = new ArrayList<>(0);
}
@Entity
@javax.persistence.Table(name = TABLE_NAME_WITH_ON_DELETE)
public static class TreeEntityWithOnDelete {
@Id
private Long id;
@ManyToOne
@JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private TreeEntityWithOnDelete parent;
@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
@OnDelete(action = OnDeleteAction.CASCADE)
private Collection<TreeEntityWithOnDelete> children = new ArrayList<>(0);
}
}

View File

@ -0,0 +1,375 @@
package org.hibernate.test.join;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import javax.persistence.*;
import java.util.List;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class OuterJoinTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{ A.class, B.class, C.class, D.class, Association.class };
}
@MappedSuperclass
public static class BaseEntity {
@Id
@Column(name = "key_id")
Long key;
String value;
@ManyToOne(optional = false)
Association association;
public BaseEntity() {
}
public BaseEntity(Long key, String value, Association association) {
this.key = key;
this.value = value;
this.association = association;
}
}
@Entity(name = "A")
@Table(name = "a")
public static class A extends BaseEntity {
@OneToOne
@PrimaryKeyJoinColumn(columnDefinition = "association_key", referencedColumnName = "key_id")
private C cAssociationByKey;
public A() {
}
public A(Long key, String value, Association association) {
super(key, value, association);
}
}
@Entity(name = "B")
@Table(name = "b")
public static class B extends BaseEntity {
public B() {
}
public B(Long key, String value, Association association) {
super(key, value, association);
}
}
@Entity(name = "C")
@Table(name = "c")
public static class C extends BaseEntity {
public C() {
}
public C(Long key, String value, Association association) {
super(key, value, association);
}
}
@Entity(name = "D")
@Table(name = "d")
public static class D extends BaseEntity {
public D() {
}
public D(Long key, String value, Association association) {
super(key, value, association);
}
}
@Entity(name = "Association")
@Table(name = "association")
public static class Association {
@Id
@Column(name = "key_id")
private Long key;
private String value;
public Association() {
}
public Association(Long key, String value) {
this.key = key;
this.value = value;
}
}
@Before
public void setUp() throws Exception {
doInJPA( this::sessionFactory, em -> {
Association association = new Association(1l, "association");
em.merge(association);
em.merge(new A(1L, "a", association));
em.merge(new A(2L, "b", association));
em.merge(new A(3L, "c", association));
em.merge(new B(1L, "d", association));
em.merge(new B(2L, "e", association));
em.merge(new B(3L, "f", association));
em.merge(new C(1L, "g", association));
em.merge(new C(2L, "h", association));
em.merge(new C(4L, "j", association));
em.merge(new D(1L, "k", association));
em.merge(new D(2L, "l", association));
em.merge(new D(4L, "m", association));
});
}
@Test
public void testJoinOrderWithRightJoin() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " +
"FROM A a " +
"INNER JOIN B b ON a.key = b.key " +
"RIGHT JOIN C c ON a.key = c.key " +
"INNER JOIN D d ON d.key = c.key " +
"ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class)
.getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
@Test
public void testJoinOrderWithRightNormalJoin() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " +
"FROM A a " +
"INNER JOIN B b ON a.key = b.key " +
"RIGHT JOIN a.cAssociationByKey c " +
"INNER JOIN D d ON d.key = c.key " +
"ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class)
.getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
@Test
public void testJoinOrderWithRightJoinWithIdDereference() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " +
"FROM A a " +
"INNER JOIN B b ON a.key = b.key AND a.association.key = b.association.key " +
"RIGHT JOIN C c ON a.key = c.key AND a.association.key = c.association.key " +
"INNER JOIN D d ON d.key = c.key AND d.association.key = c.association.key " +
"ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
@Test
public void testJoinOrderWithRightNormalJoinWithIdDereference() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " +
"FROM A a " +
"INNER JOIN B b ON a.key = b.key AND a.association.key = b.association.key " +
"RIGHT JOIN a.cAssociationByKey c ON a.key = c.key AND a.association.key = c.association.key " +
"INNER JOIN D d ON d.key = c.key AND d.association.key = c.association.key " +
"ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
@Test
public void testJoinOrderWithRightJoinWithInnerImplicitJoins() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key,b.key,c.key,d.key) AS key, a.value AS aValue, b.value AS bValue, c.value AS cValue, d.value AS dValue " +
"FROM A a JOIN a.association association_1 JOIN B b ON (EXISTS (SELECT 1 FROM b.association _synth_subquery_0 WHERE a.key = b.key AND association_1.value = _synth_subquery_0.value))" +
"RIGHT JOIN C c ON (EXISTS (SELECT 1 FROM c.association _synth_subquery_0 WHERE a.key = c.key AND association_1.value = _synth_subquery_0.value)) " +
"JOIN c.association association_5 " +
"JOIN D d ON (EXISTS (SELECT 1 FROM d.association _synth_subquery_0 WHERE d.key = c.key AND _synth_subquery_0.value = association_5.value))" +
" ORDER BY COALESCE(a.key,b.key,c.key,d.key) ASC", Tuple.class).getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
@Test
@Ignore("Hibernate doesn't support implicit joins")
public void testJoinOrderWithRightNormalJoinWithInnerImplicitJoins() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " +
"FROM A a " +
"INNER JOIN B b ON a.key = b.key AND a.association.value = b.association.value " +
"RIGHT JOIN a.cAssociationByKey c ON a.key = c.key AND a.association.value = c.association.value " +
"INNER JOIN D d ON d.key = c.key AND d.association.value = c.association.value " +
"ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
@Test
public void testJoinOrderWithRightJoinWithNonOptionalAssociationProjections() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " +
"FROM A a " +
"INNER JOIN B b ON a.key = b.key " +
"RIGHT JOIN C c ON a.key = c.key " +
"INNER JOIN D d ON d.key = c.key " +
"ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
@Test
public void testJoinOrderWithRightNormalJoinWithNonOptionalAssociationProjections() {
doInJPA( this::sessionFactory, em -> {
List<Tuple> resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " +
"FROM A a " +
"INNER JOIN B b ON a.key = b.key " +
"RIGHT JOIN a.cAssociationByKey c ON a.key = c.key " +
"INNER JOIN D d ON d.key = c.key " +
"ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList();
assertEquals(3, resultList.size());
assertEquals("a", resultList.get(0).get(1));
assertEquals("d", resultList.get(0).get(2));
assertEquals("g", resultList.get(0).get(3));
assertEquals("k", resultList.get(0).get(4));
assertEquals("b", resultList.get(1).get(1));
assertEquals("e", resultList.get(1).get(2));
assertEquals("h", resultList.get(1).get(3));
assertEquals("l", resultList.get(1).get(4));
assertNull(resultList.get(2).get(1));
assertNull(resultList.get(2).get(2));
assertEquals("j", resultList.get(2).get(3));
assertEquals("m", resultList.get(2).get(4));
});
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.test.mapping.lazytoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author Steve Ebersole
*/
@Entity( name = "Airport" )
@Table( name = "airport" )
public class Airport {
@Id
private Integer id;
private String code;
public Airport() {
}
public Airport(Integer id, String code) {
this.id = id;
this.code = code;
}
public Integer getId() {
return id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.test.mapping.lazytoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.LazyToOne;
import static javax.persistence.FetchType.LAZY;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
/**
* @author Steve Ebersole
*/
@Entity( name = "Flight" )
@Table( name = "flight" )
public class Flight {
@Id
private Integer id;
private String number;
@ManyToOne( fetch = LAZY )
private Airport origination;
@ManyToOne( fetch = LAZY )
@LazyToOne( NO_PROXY )
private Airport destination;
public Flight() {
}
public Flight(Integer id, String number) {
this.id = id;
this.number = number;
}
public Flight(Integer id, String number, Airport origination, Airport destination) {
this.id = id;
this.number = number;
this.origination = origination;
this.destination = destination;
}
public Integer getId() {
return id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Airport getOrigination() {
return origination;
}
public void setOrigination(Airport origination) {
this.origination = origination;
}
public Airport getDestination() {
return destination;
}
public void setDestination(Airport destination) {
this.destination = destination;
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.test.mapping.lazytoone;
import org.hibernate.Hibernate;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Same as {@link LazyToOneTest} except here we have bytecode-enhanced entities
* via {@link BytecodeEnhancerRunner}
*/
@RunWith( BytecodeEnhancerRunner.class )
public class InstrumentedLazyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Airport.class, Flight.class };
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
}
@Override
protected void prepareTest() throws Exception {
inTransaction(
(session) -> {
final Airport austin = new Airport( 1, "AUS" );
final Airport baltimore = new Airport( 2, "BWI" );
final Flight flight1 = new Flight( 1, "ABC-123", austin, baltimore );
final Flight flight2 = new Flight( 2, "ABC-987", baltimore, austin );
session.persist( austin );
session.persist( baltimore );
session.persist( flight1 );
session.persist( flight2 );
}
);
}
@Override
protected void cleanupTestData() throws Exception {
inTransaction(
(session) -> {
session.createQuery( "delete Flight" ).executeUpdate();
session.createQuery( "delete Airport" ).executeUpdate();
}
);
}
@Test
@FailureExpected( jiraKey = "HHH-13658", message = "Flight#origination is not treated as lazy. Not sure why exactly" )
public void testEnhancedButProxyNotAllowed() {
final StatisticsImplementor statistics = sessionFactory().getStatistics();
statistics.clear();
inTransaction(
(session) -> {
final Flight flight1 = session.byId( Flight.class ).load( 1 );
// unlike the other 2 tests we should get 2 db queries here
assertThat( statistics.getPrepareStatementCount(), is( 2L ) );
assertThat( Hibernate.isInitialized( flight1 ), is( true ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "origination" ), is( true ) );
// this should be a non-enhanced proxy
assertThat( Hibernate.isInitialized( flight1.getOrigination() ), is( false ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "destination" ), is( false ) );
// the NO_PROXY here should trigger an EAGER load
assertThat( Hibernate.isInitialized( flight1.getDestination() ), is( false ) );
}
);
}
}

View File

@ -0,0 +1,104 @@
/*
* 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.test.mapping.lazytoone;
import org.hibernate.Hibernate;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Same as {@link InstrumentedLazyToOneTest} except here we enable bytecode-enhanced proxies
*/
@RunWith( BytecodeEnhancerRunner.class )
public class InstrumentedProxyLazyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Airport.class, Flight.class };
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
}
@Override
protected void prepareTest() throws Exception {
inTransaction(
(session) -> {
final Airport austin = new Airport( 1, "AUS" );
final Airport baltimore = new Airport( 2, "BWI" );
final Flight flight1 = new Flight( 1, "ABC-123", austin, baltimore );
final Flight flight2 = new Flight( 2, "ABC-987", baltimore, austin );
session.persist( austin );
session.persist( baltimore );
session.persist( flight1 );
session.persist( flight2 );
}
);
}
@Override
protected void cleanupTestData() throws Exception {
inTransaction(
(session) -> {
session.createQuery( "delete Flight" ).executeUpdate();
session.createQuery( "delete Airport" ).executeUpdate();
}
);
}
@Test
public void testEnhancedWithProxy() {
final StatisticsImplementor statistics = sessionFactory().getStatistics();
statistics.clear();
inTransaction(
(session) -> {
final Flight flight1 = session.byId( Flight.class ).load( 1 );
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
assertThat( Hibernate.isInitialized( flight1 ), is( true ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "origination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getOrigination() ), is( false ) );
// let's make sure these `Hibernate` calls pass for the right reasons...
assertThat( flight1.getOrigination(), instanceOf( PersistentAttributeInterceptable.class ) );
final PersistentAttributeInterceptable originationProxy = (PersistentAttributeInterceptable) flight1.getOrigination();
assertThat( originationProxy.$$_hibernate_getInterceptor(), notNullValue() );
assertThat( originationProxy.$$_hibernate_getInterceptor(), instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "destination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getDestination() ), is( false ) );
// let's make sure these `Hibernate` calls pass for the right reasons...
assertThat( flight1.getDestination(), instanceOf( PersistentAttributeInterceptable.class ) );
final PersistentAttributeInterceptable destinationProxy = (PersistentAttributeInterceptable) flight1.getDestination();
assertThat( destinationProxy.$$_hibernate_getInterceptor(), notNullValue() );
assertThat( destinationProxy.$$_hibernate_getInterceptor(), instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
}
);
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.test.mapping.lazytoone;
import org.hibernate.Hibernate;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
public class LazyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Airport.class, Flight.class };
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
}
@Override
protected void prepareTest() throws Exception {
inTransaction(
(session) -> {
final Airport austin = new Airport( 1, "AUS" );
final Airport baltimore = new Airport( 2, "BWI" );
final Flight flight1 = new Flight( 1, "ABC-123", austin, baltimore );
final Flight flight2 = new Flight( 2, "ABC-987", baltimore, austin );
session.persist( austin );
session.persist( baltimore );
session.persist( flight1 );
session.persist( flight2 );
}
);
}
@Override
protected void cleanupTestData() throws Exception {
inTransaction(
(session) -> {
session.createQuery( "delete Flight" ).executeUpdate();
session.createQuery( "delete Airport" ).executeUpdate();
}
);
}
@Test
public void testNonEnhanced() {
final StatisticsImplementor statistics = sessionFactory().getStatistics();
statistics.clear();
inTransaction(
(session) -> {
final Flight flight1 = session.byId( Flight.class ).load( 1 );
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
assertThat( Hibernate.isInitialized( flight1 ), is( true ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "origination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getOrigination() ), is( false ) );
assertThat( flight1.getOrigination(), instanceOf( HibernateProxy.class ) );
assertThat( Hibernate.isPropertyInitialized( flight1, "destination" ), is( true ) );
assertThat( Hibernate.isInitialized( flight1.getDestination() ), is( false ) );
assertThat( flight1.getDestination(), instanceOf( HibernateProxy.class ) );
}
);
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.test.bytecode.javassist;
import java.text.ParseException;
import java.util.Date;
/**
* @author Steve Ebersole
*/
public class Bean {
private String someString;
private Long someLong;
private Integer someInteger;
private Date someDate;
private long somelong;
private int someint;
private Object someObject;
public String getSomeString() {
return someString;
}
public void setSomeString(String someString) {
this.someString = someString;
}
public Long getSomeLong() {
return someLong;
}
public void setSomeLong(Long someLong) {
this.someLong = someLong;
}
public Integer getSomeInteger() {
return someInteger;
}
public void setSomeInteger(Integer someInteger) {
this.someInteger = someInteger;
}
public Date getSomeDate() {
return someDate;
}
public void setSomeDate(Date someDate) {
this.someDate = someDate;
}
public long getSomelong() {
return somelong;
}
public void setSomelong(long somelong) {
this.somelong = somelong;
}
public int getSomeint() {
return someint;
}
public void setSomeint(int someint) {
this.someint = someint;
}
public Object getSomeObject() {
return someObject;
}
public void setSomeObject(Object someObject) {
this.someObject = someObject;
}
public void throwException() throws ParseException {
throw new ParseException( "you asked for it...", 0 );
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.test.bytecode.javassist;
import java.util.Date;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.Setter;
/**
* @author Steve Ebersole
*/
public class BeanReflectionHelper {
public static final Object[] TEST_VALUES = new Object[] {
"hello", new Long(1), new Integer(1), new Date(), new Long(1), new Integer(1), new Object()
};
private static final String[] getterNames = new String[7];
private static final String[] setterNames = new String[7];
private static final Class[] types = new Class[7];
static {
final PropertyAccessStrategyBasicImpl propertyAccessStrategy = new PropertyAccessStrategyBasicImpl();
PropertyAccess propertyAccess = propertyAccessStrategy.buildPropertyAccess( Bean.class, "someString" );
Getter getter = propertyAccess.getGetter();
Setter setter = propertyAccess.getSetter();
getterNames[0] = getter.getMethodName();
types[0] = getter.getReturnType();
setterNames[0] = setter.getMethodName();
propertyAccess = propertyAccessStrategy.buildPropertyAccess( Bean.class, "someLong" );
getter = propertyAccess.getGetter();
setter = propertyAccess.getSetter();
getterNames[1] = getter.getMethodName();
types[1] = getter.getReturnType();
setterNames[1] = setter.getMethodName();
propertyAccess = propertyAccessStrategy.buildPropertyAccess( Bean.class, "someInteger" );
getter = propertyAccess.getGetter();
setter = propertyAccess.getSetter();
getterNames[2] = getter.getMethodName();
types[2] = getter.getReturnType();
setterNames[2] = setter.getMethodName();
propertyAccess = propertyAccessStrategy.buildPropertyAccess( Bean.class, "someDate" );
getter = propertyAccess.getGetter();
setter = propertyAccess.getSetter();
getterNames[3] = getter.getMethodName();
types[3] = getter.getReturnType();
setterNames[3] = setter.getMethodName();
propertyAccess = propertyAccessStrategy.buildPropertyAccess( Bean.class, "somelong" );
getter = propertyAccess.getGetter();
setter = propertyAccess.getSetter();
getterNames[4] = getter.getMethodName();
types[4] = getter.getReturnType();
setterNames[4] = setter.getMethodName();
propertyAccess = propertyAccessStrategy.buildPropertyAccess( Bean.class, "someint" );
getter = propertyAccess.getGetter();
setter = propertyAccess.getSetter();
getterNames[5] = getter.getMethodName();
types[5] = getter.getReturnType();
setterNames[5] = setter.getMethodName();
propertyAccess = propertyAccessStrategy.buildPropertyAccess( Bean.class, "someObject" );
getter = propertyAccess.getGetter();
setter = propertyAccess.getSetter();
getterNames[6] = getter.getMethodName();
types[6] = getter.getReturnType();
setterNames[6] = setter.getMethodName();
}
public static String[] getGetterNames() {
return getterNames;
}
public static String[] getSetterNames() {
return setterNames;
}
public static Class[] getTypes() {
return types;
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.test.bytecode.javassist;
import org.hibernate.bytecode.internal.javassist.BulkAccessor;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
/**
* @author Steve Ebersole
*/
// Extracted from org.hibernate.test.bytecode.ReflectionOptimizerTest.
// I (Yoann) don't know what this tests does, but it's definitely specific to javassist.
public class BulkAccessorTest extends BaseUnitTestCase {
@Test
public void testBulkAccessorDirectly() {
BulkAccessor bulkAccessor = BulkAccessor.create(
Bean.class,
BeanReflectionHelper.getGetterNames(),
BeanReflectionHelper.getSetterNames(),
BeanReflectionHelper.getTypes()
);
}
}

View File

@ -9,11 +9,14 @@ package org.hibernate.envers.boot.internal;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.EntitiesConfigurator; import org.hibernate.envers.configuration.internal.EntitiesConfigurator;
import org.hibernate.envers.configuration.internal.GlobalConfiguration; import org.hibernate.envers.configuration.internal.GlobalConfiguration;
@ -36,6 +39,8 @@ import org.hibernate.service.spi.Stoppable;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED;
/** /**
* Provides central access to Envers' configuration. * Provides central access to Envers' configuration.
* *
@ -84,6 +89,11 @@ public class EnversServiceImpl implements EnversService, Configurable, Stoppable
); );
} }
this.integrationEnabled = ConfigurationHelper.getBoolean( INTEGRATION_ENABLED, configurationValues, true ); this.integrationEnabled = ConfigurationHelper.getBoolean( INTEGRATION_ENABLED, configurationValues, true );
boolean xmlMappingEnabled = ConfigurationHelper.getBoolean( XML_MAPPING_ENABLED, configurationValues, true );
if ( this.integrationEnabled && !xmlMappingEnabled ) {
throw new HibernateException( "Hibernate Envers currently requires XML mapping to be enabled. Please don't disable setting `" + XML_MAPPING_ENABLED + "`; alternatively disable Hibernate Envers." );
}
log.infof( "Envers integration enabled? : %s", integrationEnabled ); log.infof( "Envers integration enabled? : %s", integrationEnabled );
} }

View File

@ -0,0 +1,31 @@
description = 'Integration for Micrometer metrics into Hibernate as a metrics collection package'
apply from: rootProject.file( 'gradle/published-java-module.gradle' )
dependencies {
compile project( ':hibernate-core' )
compile( libraries.jpa )
compile( libraries.micrometer )
testCompile project( ':hibernate-testing' )
testCompile( libraries.mockito )
testCompile( libraries.mockito_inline )
testAnnotationProcessor( project( ':hibernate-jpamodelgen' ) )
}
sourceSets {
// resources inherently exclude sources
test {
resources {
setSrcDirs( ['src/test/java','src/test/resources'] )
}
}
testJavassist {
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
}
}
}

View File

@ -0,0 +1,369 @@
/*
* 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.stat;
import io.micrometer.core.instrument.FunctionCounter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.TimeGauge;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.lang.NonNullApi;
import io.micrometer.core.lang.NonNullFields;
import io.micrometer.core.lang.Nullable;
import org.hibernate.SessionFactory;
import org.hibernate.stat.Statistics;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;
/**
* A {@link MeterBinder} implementation that provides Hibernate metrics. It exposes the
* same statistics as would be exposed when calling {@link Statistics#logSummary()}.
*/
@NonNullApi
@NonNullFields
public class HibernateMetrics implements MeterBinder {
private static final String SESSION_FACTORY_TAG_NAME = "entityManagerFactory";
private final String cacheFactoryPrefix;
private final Iterable<Tag> tags;
@Nullable
private final Statistics statistics;
/**
* Create {@code HibernateMetrics} and bind to the specified meter registry.
*
* @param registry meter registry to use
* @param sessionFactory session factory to use
* @param sessionFactoryName session factory name as a tag value
* @param tags additional tags
*/
public static void monitor(
MeterRegistry registry,
SessionFactory sessionFactory,
String sessionFactoryName,
String... tags) {
monitor( registry, sessionFactory, sessionFactoryName, Tags.of( tags ) );
}
/**
* Create {@code HibernateMetrics} and bind to the specified meter registry.
*
* @param registry meter registry to use
* @param sessionFactory session factory to use
* @param sessionFactoryName session factory name as a tag value
* @param tags additional tags
*/
public static void monitor(
MeterRegistry registry,
SessionFactory sessionFactory,
String sessionFactoryName,
Iterable<Tag> tags) {
new HibernateMetrics( sessionFactory, sessionFactoryName, tags ).bindTo( registry );
}
/**
* Create a {@code HibernateMetrics}.
*
* @param sessionFactory session factory to use
* @param sessionFactoryName session factory name as a tag value
* @param tags additional tags
*/
public HibernateMetrics(SessionFactory sessionFactory, String sessionFactoryName, Iterable<Tag> tags) {
this.tags = Tags.concat( tags, SESSION_FACTORY_TAG_NAME, sessionFactoryName );
this.cacheFactoryPrefix = sessionFactory.getSessionFactoryOptions().getCacheRegionPrefix();
Statistics statistics = sessionFactory.getStatistics();
this.statistics = statistics.isStatisticsEnabled() ? statistics : null;
}
private void counter(
MeterRegistry registry,
String name,
String description,
ToDoubleFunction<Statistics> f,
String... extraTags) {
if ( this.statistics == null ) {
return;
}
FunctionCounter.builder( name, statistics, f )
.tags( tags )
.tags( extraTags )
.description( description )
.register( registry );
}
@Override
public void bindTo(MeterRegistry registry) {
if ( this.statistics == null ) {
return;
}
// Session statistics
counter(registry, "hibernate.sessions.open", "Sessions opened", Statistics::getSessionOpenCount );
counter(registry, "hibernate.sessions.closed", "Sessions closed", Statistics::getSessionCloseCount );
// Transaction statistics
counter(registry, "hibernate.transactions", "The number of transactions we know to have been successful",
Statistics::getSuccessfulTransactionCount, "result", "success"
);
counter(registry, "hibernate.transactions", "The number of transactions we know to have failed",
s -> s.getTransactionCount() - s.getSuccessfulTransactionCount(), "result", "failure"
);
counter(registry,
"hibernate.optimistic.failures",
"The number of StaleObjectStateExceptions that have occurred",
Statistics::getOptimisticFailureCount
);
counter(registry,
"hibernate.flushes",
"The global number of flushes executed by sessions (either implicit or explicit)",
Statistics::getFlushCount
);
counter(registry,
"hibernate.connections.obtained",
"Get the global number of connections asked by the sessions " +
"(the actual number of connections used may be much smaller depending " +
"whether you use a connection pool or not)",
Statistics::getConnectCount
);
// Statements
counter(registry, "hibernate.statements", "The number of prepared statements that were acquired",
Statistics::getPrepareStatementCount, "status", "prepared"
);
counter(registry, "hibernate.statements", "The number of prepared statements that were released",
Statistics::getCloseStatementCount, "status", "closed"
);
// Second Level Caching
// AWKWARD: getSecondLevelCacheRegionNames is the only way to retrieve a list of names
// The returned names are all qualified.
// getDomainDataRegionStatistics wants unqualified names,
// there are no "public" methods to unqualify the names
Arrays.stream( statistics.getSecondLevelCacheRegionNames() )
.map(s -> {
if ( cacheFactoryPrefix != null ) {
return s.replaceAll( cacheFactoryPrefix + ".", "" );
}
return s;
})
.filter( this::hasDomainDataRegionStatistics )
.forEach( regionName -> {
counter(registry,
"hibernate.second.level.cache.requests",
"The number of cacheable entities/collections successfully retrieved from the cache",
stats -> stats.getDomainDataRegionStatistics( regionName ).getHitCount(),
"region",
regionName,
"result",
"hit"
);
counter(registry,
"hibernate.second.level.cache.requests",
"The number of cacheable entities/collections not found in the cache and loaded from the database",
stats -> stats.getDomainDataRegionStatistics( regionName ).getMissCount(),
"region",
regionName,
"result",
"miss"
);
counter(
registry,
"hibernate.second.level.cache.puts",
"The number of cacheable entities/collections put in the cache",
stats -> stats.getDomainDataRegionStatistics( regionName ).getPutCount(),
"region",
regionName
);
} );
// Entity information
counter(registry,
"hibernate.entities.deletes",
"The number of entity deletes",
Statistics::getEntityDeleteCount
);
counter(registry,
"hibernate.entities.fetches",
"The number of entity fetches",
Statistics::getEntityFetchCount
);
counter(registry,
"hibernate.entities.inserts",
"The number of entity inserts",
Statistics::getEntityInsertCount
);
counter(registry, "hibernate.entities.loads", "The number of entity loads", Statistics::getEntityLoadCount );
counter(registry,
"hibernate.entities.updates",
"The number of entity updates",
Statistics::getEntityUpdateCount
);
// Collections
counter(registry,
"hibernate.collections.deletes",
"The number of collection deletes",
Statistics::getCollectionRemoveCount
);
counter(registry,
"hibernate.collections.fetches",
"The number of collection fetches",
Statistics::getCollectionFetchCount
);
counter(registry,
"hibernate.collections.loads",
"The number of collection loads",
Statistics::getCollectionLoadCount
);
counter(registry,
"hibernate.collections.recreates",
"The number of collections recreated",
Statistics::getCollectionRecreateCount
);
counter(registry,
"hibernate.collections.updates",
"The number of collection updates",
Statistics::getCollectionUpdateCount
);
// Natural Id cache
counter(registry,
"hibernate.cache.natural.id.requests",
"The number of cached naturalId lookups successfully retrieved from cache",
Statistics::getNaturalIdCacheHitCount,
"result",
"hit"
);
counter(registry,
"hibernate.cache.natural.id.requests",
"The number of cached naturalId lookups not found in cache",
Statistics::getNaturalIdCacheMissCount,
"result",
"miss"
);
counter(registry, "hibernate.cache.natural.id.puts", "The number of cacheable naturalId lookups put in cache",
Statistics::getNaturalIdCachePutCount
);
counter(registry,
"hibernate.query.natural.id.executions",
"The number of naturalId queries executed against the database",
Statistics::getNaturalIdQueryExecutionCount
);
TimeGauge.builder(
"hibernate.query.natural.id.executions.max",
statistics,
TimeUnit.MILLISECONDS,
Statistics::getNaturalIdQueryExecutionMaxTime
)
.description( "The maximum query time for naturalId queries executed against the database" )
.tags( tags )
.register( registry );
// Query statistics
counter(registry,
"hibernate.query.executions",
"The number of executed queries",
Statistics::getQueryExecutionCount
);
TimeGauge.builder(
"hibernate.query.executions.max",
statistics,
TimeUnit.MILLISECONDS,
Statistics::getQueryExecutionMaxTime
)
.description( "The time of the slowest query" )
.tags( tags )
.register( registry );
// Update timestamp cache
counter(registry,
"hibernate.cache.update.timestamps.requests",
"The number of timestamps successfully retrieved from cache",
Statistics::getUpdateTimestampsCacheHitCount,
"result",
"hit"
);
counter(registry,
"hibernate.cache.update.timestamps.requests",
"The number of tables for which no update timestamps was not found in cache",
Statistics::getUpdateTimestampsCacheMissCount,
"result",
"miss"
);
counter(registry, "hibernate.cache.update.timestamps.puts", "The number of timestamps put in cache",
Statistics::getUpdateTimestampsCachePutCount
);
// Query Caching
counter(registry,
"hibernate.cache.query.requests",
"The number of cached queries successfully retrieved from cache",
Statistics::getQueryCacheHitCount,
"result",
"hit"
);
counter(registry, "hibernate.cache.query.requests", "The number of cached queries not found in cache",
Statistics::getQueryCacheMissCount, "result", "miss"
);
counter(registry, "hibernate.cache.query.puts", "The number of cacheable queries put in cache",
Statistics::getQueryCachePutCount
);
counter(registry,
"hibernate.cache.query.plan",
"The global number of query plans successfully retrieved from cache",
Statistics::getQueryPlanCacheHitCount,
"result",
"hit"
);
counter(registry, "hibernate.cache.query.plan", "The global number of query plans lookups not found in cache",
Statistics::getQueryPlanCacheMissCount, "result", "miss"
);
}
private boolean hasDomainDataRegionStatistics(String regionName) {
// This appears to be a _qualified
// In 5.3, getDomainDataRegionStatistics (a new method) will throw an IllegalArgumentException
// if the region can't be resolved.
try {
return statistics.getDomainDataRegionStatistics( regionName ) != null;
}
catch (IllegalArgumentException e) {
return false;
}
}
/**
* Unwrap the {@link SessionFactory} from {@link EntityManagerFactory}.
*
* @param entityManagerFactory {@link EntityManagerFactory} to unwrap
*
* @return unwrapped {@link SessionFactory}
*/
@Nullable
private SessionFactory unwrap(EntityManagerFactory entityManagerFactory) {
try {
return entityManagerFactory.unwrap( SessionFactory.class );
}
catch (PersistenceException ex) {
return null;
}
}
}

View File

@ -0,0 +1,188 @@
/*
* 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.stat;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.lang.NonNullApi;
import io.micrometer.core.lang.NonNullFields;
import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PostLoadEventListener;
import java.util.concurrent.TimeUnit;
/**
* A {@link MeterBinder} implementation that provides Hibernate query metrics. It exposes the
* same statistics as would be exposed when calling {@link Statistics#getQueryStatistics(String)}.
* Note that only SELECT queries are recorded in {@link QueryStatistics}.
* <p>
* Be aware of the potential for high cardinality of unique Hibernate queries executed by your
* application when considering using this {@link MeterBinder}.
*/
@NonNullApi
@NonNullFields
public class HibernateQueryMetrics implements MeterBinder {
private static final String SESSION_FACTORY_TAG_NAME = "entityManagerFactory";
private final Iterable<Tag> tags;
private final SessionFactory sessionFactory;
/**
* Create {@code HibernateQueryMetrics} and bind to the specified meter registry.
*
* @param registry meter registry to use
* @param sessionFactory session factory to use
* @param sessionFactoryName session factory name as a tag value
* @param tags additional tags
*/
public static void monitor(
MeterRegistry registry,
SessionFactory sessionFactory,
String sessionFactoryName,
String... tags) {
monitor( registry, sessionFactory, sessionFactoryName, Tags.of( tags ) );
}
/**
* Create {@code HibernateQueryMetrics} and bind to the specified meter registry.
*
* @param registry meter registry to use
* @param sessionFactory session factory to use
* @param sessionFactoryName session factory name as a tag value
* @param tags additional tags
*/
public static void monitor(
MeterRegistry registry,
SessionFactory sessionFactory,
String sessionFactoryName,
Iterable<Tag> tags) {
new HibernateQueryMetrics( sessionFactory, sessionFactoryName, tags ).bindTo( registry );
}
/**
* Create a {@code HibernateQueryMetrics}.
*
* @param sessionFactory session factory to use
* @param sessionFactoryName session factory name as a tag value
* @param tags additional tags
*/
public HibernateQueryMetrics(SessionFactory sessionFactory, String sessionFactoryName, Iterable<Tag> tags) {
this.tags = Tags.concat( tags, SESSION_FACTORY_TAG_NAME, sessionFactoryName );
this.sessionFactory = sessionFactory;
}
@Override
public void bindTo(MeterRegistry meterRegistry) {
if ( sessionFactory instanceof SessionFactoryImplementor ) {
EventListenerRegistry eventListenerRegistry = ( (SessionFactoryImplementor) sessionFactory ).getServiceRegistry()
.getService( EventListenerRegistry.class );
MetricsEventHandler metricsEventHandler = new MetricsEventHandler( meterRegistry );
eventListenerRegistry.appendListeners( EventType.POST_LOAD, metricsEventHandler );
}
}
class MetricsEventHandler implements PostLoadEventListener {
private final MeterRegistry meterRegistry;
MetricsEventHandler(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public void onPostLoad(PostLoadEvent event) {
registerQueryMetric( event.getSession().getFactory().getStatistics() );
}
void registerQueryMetric(Statistics statistics) {
for ( String query : statistics.getQueries() ) {
QueryStatistics queryStatistics = statistics.getQueryStatistics( query );
FunctionCounter.builder(
"hibernate.query.cache.requests",
queryStatistics,
QueryStatistics::getCacheHitCount
)
.tags( tags )
.tags( "result", "hit", "query", query )
.description( "Number of query cache hits" )
.register( meterRegistry );
FunctionCounter.builder(
"hibernate.query.cache.requests",
queryStatistics,
QueryStatistics::getCacheMissCount
)
.tags( tags )
.tags( "result", "miss", "query", query )
.description( "Number of query cache misses" )
.register( meterRegistry );
FunctionCounter.builder(
"hibernate.query.cache.puts",
queryStatistics,
QueryStatistics::getCachePutCount
)
.tags( tags )
.tags( "query", query )
.description( "Number of cache puts for a query" )
.register( meterRegistry );
FunctionTimer.builder(
"hibernate.query.execution.total",
queryStatistics,
QueryStatistics::getExecutionCount,
QueryStatistics::getExecutionTotalTime,
TimeUnit.MILLISECONDS
)
.tags( tags )
.tags( "query", query )
.description( "Query executions" )
.register( meterRegistry );
TimeGauge.builder(
"hibernate.query.execution.max",
queryStatistics,
TimeUnit.MILLISECONDS,
QueryStatistics::getExecutionMaxTime
)
.tags( tags )
.tags( "query", query )
.description( "Query maximum execution time" )
.register( meterRegistry );
TimeGauge.builder(
"hibernate.query.execution.min",
queryStatistics,
TimeUnit.MILLISECONDS,
QueryStatistics::getExecutionMinTime
)
.tags( tags )
.tags( "query", query )
.description( "Query minimum execution time" )
.register( meterRegistry );
FunctionCounter.builder(
"hibernate.query.execution.rows",
queryStatistics,
QueryStatistics::getExecutionRowCount
)
.tags( tags )
.tags( "query", query )
.description( "Number of rows processed for a query" )
.register( meterRegistry );
}
}
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.test.stat;
import javax.persistence.Basic;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.hibernate.annotations.NaturalId;
@Entity
@Table( name = "t_acct" )
public class Account {
@EmbeddedId
private AccountId accountId;
@Basic( optional = false )
@NaturalId
private String shortCode;
protected Account() {
}
public Account(AccountId accountId, String shortCode) {
this.accountId = accountId;
this.shortCode = shortCode;
}
public AccountId getAccountId() {
return accountId;
}
public String getShortCode() {
return shortCode;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.test.stat;
import javax.persistence.Embeddable;
@Embeddable
public class AccountId implements java.io.Serializable {
private final int id;
protected AccountId() {
this.id = 0;
}
public AccountId(int id) {
this.id = id;
}
public int intValue() {
return id;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AccountId other = (AccountId) obj;
if (other != null && id != other.id)
return false;
return true;
}
}

View File

@ -0,0 +1,208 @@
/*
* 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.test.stat;
import java.util.List;
import java.util.Map;
import javax.persistence.Cacheable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.Session;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.stat.HibernateMetrics;
import org.hibernate.testing.cache.CachingRegionFactory;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
/**
* @author Erin Schnabel
* @author Steve Ebersole
*/
public class MicrometerCacheStatisticsTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void applyMetadataSources(MetadataSources metadataSources) {
super.applyMetadataSources( metadataSources );
metadataSources.addAnnotatedClass( Person.class );
metadataSources.addAnnotatedClass( Account.class );
metadataSources.addAnnotatedClass( AccountId.class );
}
private static final String REGION = "TheRegion";
private static final String PREFIX = "test";
private SimpleMeterRegistry registry = new SimpleMeterRegistry();
private HibernateMetrics hibernateMetrics;
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, true );
ssrb.applySetting( AvailableSettings.USE_QUERY_CACHE, true );
ssrb.applySetting( AvailableSettings.CACHE_REGION_FACTORY, new CachingRegionFactory() );
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
}
@Override
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, "true" );
settings.put( AvailableSettings.USE_QUERY_CACHE, "true" );
settings.put( AvailableSettings.CACHE_REGION_FACTORY, new CachingRegionFactory() );
settings.put( AvailableSettings.GENERATE_STATISTICS, "true" );
settings.put( AvailableSettings.HBM2DDL_AUTO, "create-drop" );
settings.put( AvailableSettings.SESSION_FACTORY_NAME, "something" );
settings.put( AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, "false" );
}
@Before
public void setUpMetrics() {
hibernateMetrics = new HibernateMetrics(sessionFactory(),
sessionFactory().getName(),
Tags.empty() );
hibernateMetrics.bindTo( registry );
}
@After
public void cleanUpMetrics() {
registry.clear();
}
@Test
public void testMicrometerMetrics() {
Assert.assertNotNull(registry.get("hibernate.sessions.open").functionCounter());
Assert.assertNotNull(registry.get("hibernate.sessions.closed").functionCounter());
Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "success").functionCounter());
Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "failure").functionCounter());
Assert.assertNotNull(registry.get("hibernate.optimistic.failures").functionCounter());
Assert.assertNotNull(registry.get("hibernate.flushes").functionCounter());
Assert.assertNotNull(registry.get("hibernate.connections.obtained").functionCounter());
Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "prepared").functionCounter());
Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "closed").functionCounter());
Assert.assertNotNull(registry.get("hibernate.second.level.cache.requests").tags("result", "hit", "region", REGION));
Assert.assertNotNull(registry.get("hibernate.second.level.cache.requests").tags("result", "miss", "region", REGION));
Assert.assertNotNull(registry.get("hibernate.second.level.cache.puts").tags("region", REGION).functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.deletes").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.fetches").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.inserts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.loads").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.updates").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.deletes").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.fetches").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.loads").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.recreates").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.updates").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "miss").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.natural.id.puts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions").functionCounter());
Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions.max").timeGauge());
Assert.assertNotNull(registry.get("hibernate.query.executions").functionCounter());
Assert.assertNotNull(registry.get("hibernate.query.executions.max").timeGauge());
Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "miss").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.puts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "miss").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.puts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "miss").functionCounter());
// prepare some test data...
Session session = openSession();
session.beginTransaction();
Person person = new Person( 1, "testAcct");
session.save( person );
session.getTransaction().commit();
session.close();
Assert.assertEquals( 1, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.entities.inserts").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.cache.natural.id.puts").functionCounter().count(), 0);
Assert.assertEquals(2, registry.get("hibernate.second.level.cache.puts").tags("region", REGION).functionCounter().count(), 0);
final String queryString = "select p from Person p";
inTransaction(
// Only way to generate query region (to be accessible via stats) is to execute the query
s -> s.createQuery( queryString ).setCacheable( true ).setCacheRegion( REGION ).list()
);
Assert.assertEquals( 2, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
Assert.assertEquals( 2, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
Assert.assertEquals( 0, registry.get("hibernate.entities.deletes").functionCounter().count(), 0 );
Assert.assertEquals( 2, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.cache.natural.id.puts").functionCounter().count(), 0);
Assert.assertEquals(3, registry.get("hibernate.second.level.cache.puts").tags("region", REGION).functionCounter().count(), 0);
// clean up
session = openSession();
session.beginTransaction();
session.delete( person );
session.getTransaction().commit();
session.close();
Assert.assertEquals( 3, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
Assert.assertEquals( 3, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.entities.deletes").functionCounter().count(), 0 );
Assert.assertEquals( 3, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
}
@Entity( name = "Person" )
@Table( name = "persons" )
@Cacheable
@Cache( region = REGION, usage = CacheConcurrencyStrategy.READ_WRITE )
@NaturalIdCache( region = REGION )
public static class Person {
@Id
public Integer id;
@NaturalId
public String name;
protected Person() {
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@ElementCollection
@Cache( region = REGION, usage = CacheConcurrencyStrategy.READ_WRITE )
public List<String> nickNames;
}
}

View File

@ -0,0 +1,147 @@
/*
* 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.test.stat;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.stat.HibernateMetrics;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.search.MeterNotFoundException;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import static org.junit.Assert.assertEquals;
/**
* @author Erin Schnabel
* @author Donnchadh O Donnabhain
*/
public class MicrometerStatisticsTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Account.class, AccountId.class };
}
private SimpleMeterRegistry registry = new SimpleMeterRegistry();
private HibernateMetrics hibernateMetrics;
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" );
configuration.setProperty( Environment.USE_QUERY_CACHE, "false" );
configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
configuration.setProperty( Environment.SESSION_FACTORY_NAME, "something" );
configuration.setProperty( Environment.SESSION_FACTORY_NAME_IS_JNDI, "false" );
}
@Before
public void setUpMetrics() {
hibernateMetrics = new HibernateMetrics(sessionFactory(),
sessionFactory().getName(),
Tags.empty() );
hibernateMetrics.bindTo( registry );
}
@After
public void cleanUpMetrics() {
registry.clear();
}
@Test
public void testMicrometerMetrics() {
Assert.assertNotNull(registry.get("hibernate.sessions.open").functionCounter());
Assert.assertNotNull(registry.get("hibernate.sessions.closed").functionCounter());
Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "success").functionCounter());
Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "failure").functionCounter());
Assert.assertNotNull(registry.get("hibernate.optimistic.failures").functionCounter());
Assert.assertNotNull(registry.get("hibernate.flushes").functionCounter());
Assert.assertNotNull(registry.get("hibernate.connections.obtained").functionCounter());
Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "prepared").functionCounter());
Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "closed").functionCounter());
// Second level cache disabled
verifyMeterNotFoundException("hibernate.second.level.cache.requests");
verifyMeterNotFoundException("hibernate.second.level.cache.puts");
Assert.assertNotNull(registry.get("hibernate.entities.deletes").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.fetches").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.inserts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.loads").functionCounter());
Assert.assertNotNull(registry.get("hibernate.entities.updates").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.deletes").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.fetches").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.loads").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.recreates").functionCounter());
Assert.assertNotNull(registry.get("hibernate.collections.updates").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "miss").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.natural.id.puts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions").functionCounter());
Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions.max").timeGauge());
Assert.assertNotNull(registry.get("hibernate.query.executions").functionCounter());
Assert.assertNotNull(registry.get("hibernate.query.executions.max").timeGauge());
Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "miss").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.puts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "miss").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.puts").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "hit").functionCounter());
Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "miss").functionCounter());
// prepare some test data...
Session session = openSession();
session.beginTransaction();
Account account = new Account( new AccountId( 1), "testAcct");
session.save( account );
session.getTransaction().commit();
session.close();
Assert.assertEquals( 1, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.entities.inserts").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
// clean up
session = openSession();
session.beginTransaction();
session.delete( account );
session.getTransaction().commit();
session.close();
Assert.assertEquals( 2, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
Assert.assertEquals( 2, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
Assert.assertEquals( 1, registry.get("hibernate.entities.deletes").functionCounter().count(), 0 );
Assert.assertEquals( 2, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
}
void verifyMeterNotFoundException(String name) {
try {
registry.get(name).meter();
Assert.fail(name + " should not have been found");
} catch(MeterNotFoundException mnfe) {
}
}
}

View File

@ -0,0 +1,27 @@
#
# 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>.
#
hibernate.dialect @db.dialect@
hibernate.connection.driver_class @jdbc.driver@
hibernate.connection.url @jdbc.url@
hibernate.connection.username @jdbc.user@
hibernate.connection.password @jdbc.pass@
hibernate.connection.pool_size 5
hibernate.show_sql false
hibernate.format_sql true
hibernate.max_fetch_depth 5
hibernate.cache.region_prefix hibernate.test
hibernate.cache.region.factory_class org.hibernate.testing.cache.CachingRegionFactory
javax.persistence.validation.mode=NONE
hibernate.service.allow_crawling=false
hibernate.session.events.log=true
hibernate.hql.bulk_id_strategy.global_temporary.drop_tables=true

View File

@ -0,0 +1,19 @@
#
# 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>.
#
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=info, stdout
log4j.logger.org.hibernate.stat=trace
log4j.logger.org.hibernate.tool.hbm2ddl=trace
log4j.logger.org.hibernate.SQL=debug
log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=trace
log4j.logger.org.hibernate.type.descriptor.sql.BasicExtractor=trace

View File

@ -15,3 +15,15 @@ dependencies {
compile( libraries.proxool ) compile( libraries.proxool )
testCompile project( ':hibernate-testing' ) testCompile project( ':hibernate-testing' )
} }
test {
if ( gradle.ext.javaVersions.test.launcher.asInt() >= 9 ) {
// Proxool needs this to define classes for some reason. Stack trace:
// at org.logicalcobwebs.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:372)
// at org.logicalcobwebs.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:193)
// at org.logicalcobwebs.cglib.core.KeyFactory$Generator.create(KeyFactory.java:177)
// at org.logicalcobwebs.cglib.core.KeyFactory.create(KeyFactory.java:149)
// at org.logicalcobwebs.cglib.proxy.Enhancer.<clinit>(Enhancer.java:96)
jvmArgs( ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] )
}
}

View File

@ -106,7 +106,7 @@ include 'hibernate-agroal'
include 'hibernate-jcache' include 'hibernate-jcache'
include 'hibernate-ehcache' include 'hibernate-ehcache'
include 'hibernate-infinispan' include 'hibernate-infinispan'
include 'hibernate-micrometer'
include 'hibernate-graalvm' include 'hibernate-graalvm'
// The plugin used to generate Java modules was compiled using JDK11. // The plugin used to generate Java modules was compiled using JDK11.

View File

@ -134,3 +134,9 @@ else {
" Using the JDK that runs Gradle for Groovy compilation." ) " Using the JDK that runs Gradle for Groovy compilation." )
} }
tasks.test {
if ( gradle.ext.javaVersions.test.launcher.asInt() >= 9 ) {
// Needs add-opens because Gradle uses illegal accesses to inject... mocks? Something like that.
jvmArgs( ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] )
}
}