diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 70e915f206..6b0c224241 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,19 +21,18 @@ While we try to keep requirements for contributing to a minimum, there are a few we ask that you mind. For code contributions, these guidelines include: -* respect the project code style - find templates for [Eclipse](https://community.jboss.org/docs/DOC-16649) - and [IntelliJ IDEA](https://community.jboss.org/docs/DOC-15468) +* 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) * 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 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 - the project to protect against regressions. + the project to protect against regressions * if applicable, documentation is updated to reflect the introduced changes * the code compiles and the tests pass (`./gradlew clean build`) 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 -IDEA IDEs. Ideally these contributions would also have a corresponding JIRA issue, although this +to use of tabs - as mentioned above, code style templates are available for both IntelliJ IDEA and Eclipse +IDEs. Ideally these contributions would also have a corresponding JIRA issue, although this 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 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 [GitHub account](https://github.com/signup/free) -* [Fork](https://help.github.com/articles/fork-a-repo) the Hibernate repository. As discussed in +* make sure you have a [Hibernate JIRA account](https://hibernate.atlassian.net) +* 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 the linked page, this also includes: - * [Set](https://help.github.com/articles/set-up-git) up your local git install - * Clone your fork + * [set up your local git install](https://help.github.com/articles/set-up-git) + * clone your fork * See the wiki pages for setting up your IDE, whether you use [IntelliJ IDEA](https://community.jboss.org/wiki/ContributingToHibernateUsingIntelliJ) or [Eclipse](https://community.jboss.org/wiki/ContributingToHibernateUsingEclipse)(1). @@ -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, although this is more of a mnemonic strategy than a hard-and-fast rule - but doing so helps: * 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._ @@ -69,17 +68,17 @@ on the JIRA HHH-123 : `git checkout -b HHH-123 master` ## Code -Do yo thing! +Do your thing! ## Commit -* Make commits of logical units. -* 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. -* Make sure you have added the necessary tests for your changes. -* Run _all_ the tests to assure nothing else was accidentally broken. -* Make sure your source does not violate the checkstyles. +* make commits of logical units +* 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 +* make sure you have added the necessary tests for your changes +* run _all_ the tests to assure nothing else was accidentally broken +* make sure your source does not violate the _checkstyles_ _Prior to committing, if you want to pull in the latest upstream changes (highly 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 -* 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) -* Update the JIRA issue, adding a comment including a link to the created pull request - _if the JIRA key was not used in the commit message_. +* 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) +* update the JIRA issue by providing the PR link in the **Pull Request** column on the right 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 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 - 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 - 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 (1) 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 diff --git a/gradle.properties b/gradle.properties index 2875c417b4..b8ec248b50 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,7 @@ # 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 -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.launcher.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 @@ -13,4 +14,4 @@ toolchain.launcher.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOf org.gradle.java.installations.auto-detect=false # We can't rely on Gradle's auto-download of JDKs as it doesn't support EA releases. # See https://github.com/gradle/gradle/blob/fc7ea24f3c525d8d12a4346eb0f15976a6be9414/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/install/internal/AdoptOpenJdkRemoteBinary.java#L114 -org.gradle.java.installations.auto-download=false \ No newline at end of file +org.gradle.java.installations.auto-download=false diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index 7a3d0ad7be..07951fc82a 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -251,7 +251,7 @@ if ( gradle.ext.javaToolchainEnabled ) { } // Configure JVM Options - jvmArgs.addAll( getProperty( 'toolchain.launcher.jvmargs' ).toString().split( ' ' ) ) + jvmArgs( getProperty( 'toolchain.launcher.jvmargs' ).toString().split( ' ' ) ) // Display version of Java tools doFirst { diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 43df18373d..3e7dcce669 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -23,7 +23,7 @@ ext { elVersion = '3.0.1-b09' cdiVersion = '2.0' - weldVersion = '3.0.0.Final' + weldVersion = '3.1.5.Final' javassistVersion = '3.27.0-GA' byteBuddyVersion = '1.10.17' @@ -49,6 +49,8 @@ ext { antlrVersion = '4.8-1' + micrometerVersion = '1.6.1' + libraries = [ // Ant ant: 'org.apache.ant:ant:1.8.2', @@ -155,6 +157,7 @@ ext { vibur: "org.vibur:vibur-dbcp:25.0", agroal_api: "io.agroal:agroal-api:${agroalVersion}", agroal_pool: "io.agroal:agroal-pool:${agroalVersion}", + micrometer: "io.micrometer:micrometer-core:1.6.1", atomikos: "com.atomikos:transactions:4.0.6", atomikos_jta: "com.atomikos:transactions-jta:4.0.6", diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index c58479afd0..ed4cd7a6a7 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -263,6 +263,15 @@ task generateEnversStaticMetamodel( test { 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 -> //println "Starting test: " + descriptor } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java index 178af0d694..8aaa551f12 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java @@ -201,23 +201,24 @@ public class ScanningCoordinator { nonLocatedMappingFileNames.addAll( explicitMappingFileNames ); } - for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) { - 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 ) - ); + if ( xmlMappingBinderAccess != null ) { // xml mapping is not disabled + for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) { + managedResources.addXmlBinding( xmlMappingBinderAccess.bind( mappingFileDescriptor.getStreamAccess() ) ); + nonLocatedMappingFileNames.remove( mappingFileDescriptor.getName() ); } - 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index 63061f03a8..55aa22db11 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -289,28 +289,30 @@ public class MetadataBuildingProcess { metadataCollector.processSecondPasses( rootMetadataBuildingContext ); - Iterable producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class ); - if ( producers != null ) { - final EntityHierarchyBuilder hierarchyBuilder = new EntityHierarchyBuilder(); -// final MappingBinder mappingBinder = new MappingBinder( true ); - // We need to disable validation here. It seems Envers is not producing valid (according to schema) XML - final MappingBinder mappingBinder = options.isXmlMappingEnabled() ? new MappingBinder( classLoaderService, false ) : null; - for ( AdditionalJaxbMappingProducer producer : producers ) { - log.tracef( "Calling AdditionalJaxbMappingProducer : %s", producer ); - Collection additionalMappings = producer.produceAdditionalMappings( - metadataCollector, - jandexView, - mappingBinder, - rootMetadataBuildingContext - ); - for ( MappingDocument mappingDocument : additionalMappings ) { - hierarchyBuilder.indexMappingDocument( mappingDocument ); + if ( options.isXmlMappingEnabled() ) { + Iterable producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class ); + if ( producers != null ) { + final EntityHierarchyBuilder hierarchyBuilder = new EntityHierarchyBuilder(); + // final MappingBinder mappingBinder = new MappingBinder( true ); + // We need to disable validation here. It seems Envers is not producing valid (according to schema) XML + final MappingBinder mappingBinder = new MappingBinder( classLoaderService, false ); + for ( AdditionalJaxbMappingProducer producer : producers ) { + log.tracef( "Calling AdditionalJaxbMappingProducer : %s", producer ); + Collection additionalMappings = producer.produceAdditionalMappings( + metadataCollector, + jandexView, + mappingBinder, + rootMetadataBuildingContext + ); + for ( MappingDocument mappingDocument : additionalMappings ) { + hierarchyBuilder.indexMappingDocument( mappingDocument ); + } } - } - ModelBinder binder = ModelBinder.prepare( rootMetadataBuildingContext ); - for ( EntityHierarchySourceImpl entityHierarchySource : hierarchyBuilder.buildHierarchies() ) { - binder.bindEntityHierarchy( entityHierarchySource ); + ModelBinder binder = ModelBinder.prepare( rootMetadataBuildingContext ); + for ( EntityHierarchySourceImpl entityHierarchySource : hierarchyBuilder.buildHierarchies() ) { + binder.bindEntityHierarchy( entityHierarchySource ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java index 97f768d4c4..4894848cd9 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java @@ -30,6 +30,7 @@ import org.hibernate.proxy.ProxyConfiguration; import org.hibernate.proxy.ProxyFactory; import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; import net.bytebuddy.TypeCache; import net.bytebuddy.asm.AsmVisitorWrapper.ForDeclaredMethods; import net.bytebuddy.asm.MemberSubstitution; @@ -71,7 +72,11 @@ public final class ByteBuddyState { private final TypeCache basicProxyCache; 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.Sort.WEAK ); this.basicProxyCache = new TypeCache.WithInlineExpunction( TypeCache.Sort.WEAK ); diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java index 15f4c1ecc8..814d8c293b 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java @@ -20,6 +20,7 @@ import org.hibernate.bytecode.spi.ProxyFactoryFactory; import org.hibernate.bytecode.spi.ReflectionOptimizer; import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper; +import net.bytebuddy.ClassFileVersion; import net.bytebuddy.NamingStrategy; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -49,8 +50,21 @@ public class BytecodeProviderImpl implements BytecodeProvider { 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() { - 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 ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index 71412a28b0..acb407af4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -51,6 +51,7 @@ import org.hibernate.annotations.LazyGroup; import org.hibernate.annotations.Loader; import org.hibernate.annotations.ManyToAny; import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import org.hibernate.annotations.OptimisticLock; import org.hibernate.annotations.OrderBy; import org.hibernate.annotations.Parameter; @@ -104,6 +105,7 @@ import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.mapping.Selectable; import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.Table; @@ -557,7 +559,9 @@ public abstract class CollectionBinder { } //TODO reduce tableBinder != null and oneToMany XClass collectionType = getCollectionType(); - if ( inheritanceStatePerClass == null) throw new AssertionFailure( "inheritanceStatePerClass not set" ); + if ( inheritanceStatePerClass == null) { + throw new AssertionFailure( "inheritanceStatePerClass not set" ); + } SecondPass sp = getSecondPass( fkJoinColumns, joinColumns, @@ -606,7 +610,9 @@ public abstract class CollectionBinder { binder.setUpdatable( updatable ); Property prop = binder.makeProperty(); //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 ); } @@ -614,7 +620,7 @@ public abstract class CollectionBinder { boolean hadOrderBy = false; boolean hadExplicitSort = false; - Class comparatorClass = null; + Class> comparatorClass = null; if ( jpaOrderBy == null && sqlOrderBy == null ) { if ( deprecatedSort != null ) { @@ -779,8 +785,9 @@ public abstract class CollectionBinder { final TableBinder assocTableBinder, final MetadataBuildingContext buildingContext) { return new CollectionSecondPass( buildingContext, collection ) { + @SuppressWarnings("rawtypes") @Override - public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas) throws MappingException { + public void secondPass(Map persistentClasses, Map inheritedMetas) throws MappingException { bindStarToManySecondPass( persistentClasses, collType, @@ -803,7 +810,7 @@ public abstract class CollectionBinder { * return true if it's a Fk, false if it's an association table */ protected boolean bindStarToManySecondPass( - Map persistentClasses, + Map persistentClasses, XClass collType, Ejb3JoinColumn[] fkJoinColumns, Ejb3JoinColumn[] keyColumns, @@ -815,7 +822,7 @@ public abstract class CollectionBinder { TableBinder associationTableBinder, boolean ignoreNotFound, MetadataBuildingContext buildingContext) { - PersistentClass persistentClass = (PersistentClass) persistentClasses.get( collType.getName() ); + PersistentClass persistentClass = persistentClasses.get( collType.getName() ); boolean reversePropertyInJoin = false; if ( persistentClass != null && StringHelper.isNotEmpty( this.mappedBy ) ) { try { @@ -876,7 +883,7 @@ public abstract class CollectionBinder { protected void bindOneToManySecondPass( Collection collection, - Map persistentClasses, + Map persistentClasses, Ejb3JoinColumn[] fkJoinColumns, XClass collectionType, boolean cascadeDeleteEnabled, @@ -898,7 +905,7 @@ public abstract class CollectionBinder { oneToMany.setIgnoreNotFound( ignoreNotFound ); String assocClass = oneToMany.getReferencedEntityName(); - PersistentClass associatedClass = (PersistentClass) persistentClasses.get( assocClass ); + PersistentClass associatedClass = persistentClasses.get( assocClass ); if ( jpaOrderBy != null ) { final String orderByFragment = buildOrderByClauseFromHql( jpaOrderBy.value(), @@ -1215,8 +1222,8 @@ public abstract class CollectionBinder { key.setForeignKeyName( StringHelper.nullIfEmpty( collectionTableAnn.foreignKey().name() ) ); key.setForeignKeyDefinition( StringHelper.nullIfEmpty( collectionTableAnn.foreignKey().foreignKeyDefinition() ) ); if ( key.getForeignKeyName() == null && - key.getForeignKeyDefinition() == null && - collectionTableAnn.joinColumns().length == 1 ) { + key.getForeignKeyDefinition() == null && + collectionTableAnn.joinColumns().length == 1 ) { JoinColumn joinColumn = collectionTableAnn.joinColumns()[0]; key.setForeignKeyName( StringHelper.nullIfEmpty( joinColumn.foreignKey().name() ) ); key.setForeignKeyDefinition( StringHelper.nullIfEmpty( joinColumn.foreignKey().foreignKeyDefinition() ) ); @@ -1261,15 +1268,25 @@ public abstract class CollectionBinder { key.setForeignKeyDefinition( StringHelper.nullIfEmpty( fkOverride.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() ) ); + final OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class ); + final OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class ); + if ( oneToManyAnn != null && !oneToManyAnn.mappedBy().isEmpty() + && ( onDeleteAnn == null || onDeleteAnn.action() != OnDeleteAction.CASCADE ) ) { + // foreign key should be up to @ManyToOne side + // @OnDelete generate "on delete cascade" foreign key + key.setForeignKeyName( "none" ); + } + 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( Collection collValue, - Map persistentClasses, + Map persistentClasses, Ejb3JoinColumn[] joinColumns, Ejb3JoinColumn[] inverseJoinColumns, Ejb3Column[] elementColumns, @@ -1299,7 +1316,7 @@ public abstract class CollectionBinder { 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 ); boolean isCollectionOfEntities = collectionEntity != null; @@ -1732,13 +1749,13 @@ public abstract class CollectionBinder { final String mappedBy = columns[0].getMappedBy(); if ( StringHelper.isNotEmpty( mappedBy ) ) { final Property property = referencedEntity.getRecursiveProperty( mappedBy ); - Iterator mappedByColumns; + Iterator mappedByColumns; if ( property.getValue() instanceof Collection ) { mappedByColumns = ( (Collection) property.getValue() ).getKey().getColumnIterator(); } else { //find the appropriate reference key, can be in a join - Iterator joinsIt = referencedEntity.getJoinIterator(); + Iterator joinsIt = referencedEntity.getJoinIterator(); KeyValue key = null; while ( joinsIt.hasNext() ) { Join join = (Join) joinsIt.next(); @@ -1747,7 +1764,9 @@ public abstract class CollectionBinder { break; } } - if ( key == null ) key = property.getPersistentClass().getIdentifier(); + if ( key == null ) { + key = property.getPersistentClass().getIdentifier(); + } mappedByColumns = key.getColumnIterator(); } while ( mappedByColumns.hasNext() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index ea5efce1ef..3931ff2c26 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -788,6 +788,13 @@ public class PostgreSQLDialect extends Dialect { super.augmentRecognizedTableTypes( tableTypesList ); if ( getVersion() >= 930 ) { tableTypesList.add( "MATERIALIZED VIEW" ); + + /* + PostgreSQL 10 and later adds support for Partition table. + */ + if ( getVersion() >= 1000 ) { + tableTypesList.add( "PARTITIONED TABLE" ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index ee1eb0327d..1c028e3999 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -2540,7 +2540,8 @@ public abstract class AbstractEntityPersister mutablePropertiesIndexes.stream().forEach( i -> { // This is kindly borrowed from org.hibernate.type.TypeHelper.findDirty 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] && propertyTypes[i].isDirty( previousState[i], diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/Airport.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/Airport.java new file mode 100644 index 0000000000..c8680f50d0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/Airport.java @@ -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 . + */ +package org.hibernate.orm.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; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/Flight.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/Flight.java new file mode 100644 index 0000000000..92d38a1300 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/Flight.java @@ -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 . + */ +package org.hibernate.orm.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; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/InstrumentedLazyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/InstrumentedLazyToOneTest.java new file mode 100644 index 0000000000..7f309a4474 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/InstrumentedLazyToOneTest.java @@ -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 . + */ +package org.hibernate.orm.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 ) ); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/InstrumentedProxyLazyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/InstrumentedProxyLazyToOneTest.java new file mode 100644 index 0000000000..22629e8992 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/InstrumentedProxyLazyToOneTest.java @@ -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 . + */ +package org.hibernate.orm.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 ) ); + } + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/LazyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/LazyToOneTest.java new file mode 100644 index 0000000000..d9e1044a8a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/lazytoone/LazyToOneTest.java @@ -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 . + */ +package org.hibernate.orm.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 ) ); + } + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneEagerDerivedIdFetchModeSelectTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneEagerDerivedIdFetchModeSelectTest.java new file mode 100644 index 0000000000..7c2ef517c0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneEagerDerivedIdFetchModeSelectTest.java @@ -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 . + */ +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; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneLazyDerivedIdFetchModeSelectTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneLazyDerivedIdFetchModeSelectTest.java new file mode 100644 index 0000000000..aec7d4a826 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneLazyDerivedIdFetchModeSelectTest.java @@ -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 . + */ +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; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java index 1dcf423d2e..33fe97249e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java @@ -51,6 +51,85 @@ public class OneToOneWithDerivedIdentityTest extends BaseCoreFunctionalTestCase 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 @TestForIssue( jiraKey = "HHH-10476") public void testInsertFooAndBarWithDerivedIdPC() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/unidirectional/OneToOneEagerDerivedIdFetchModeSelectTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/unidirectional/OneToOneEagerDerivedIdFetchModeSelectTest.java new file mode 100644 index 0000000000..95b02f4d84 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/unidirectional/OneToOneEagerDerivedIdFetchModeSelectTest.java @@ -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 . + */ +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; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/unidirectional/OneToOneLazyDerivedIdFetchModeSelectTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/unidirectional/OneToOneLazyDerivedIdFetchModeSelectTest.java new file mode 100644 index 0000000000..8afe8d29b3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/unidirectional/OneToOneLazyDerivedIdFetchModeSelectTest.java @@ -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 . + */ +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; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/selectbeforeupdate/UpdateDetachedTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/selectbeforeupdate/UpdateDetachedTest.java index 673607968f..63d8411a6e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/selectbeforeupdate/UpdateDetachedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/selectbeforeupdate/UpdateDetachedTest.java @@ -8,6 +8,7 @@ package org.hibernate.test.annotations.selectbeforeupdate; import java.util.HashSet; import java.util.Set; +import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Embeddable; import javax.persistence.Entity; @@ -280,6 +281,7 @@ public class UpdateDetachedTest extends BaseCoreFunctionalTestCase{ @Embeddable public static class Comment { + @Column(name = "bar_comment") private String comment; private String author; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/dirty/DirtyTrackingPersistTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/dirty/DirtyTrackingPersistTest.java new file mode 100644 index 0000000000..f88d4beb6e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/dirty/DirtyTrackingPersistTest.java @@ -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 . + */ +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 nextRevUNs; + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "clicId") + @OrderBy("id asc") + protected List 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 getNextRevUNs() { + return nextRevUNs; + } + + public void setNextRevUNs(List nextRevUNs) { + this.nextRevUNs = nextRevUNs; + } + + public List getLineItems() { + return lineItems; + } + + public void setLineItems(List 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++; + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/AbstractKey.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/AbstractKey.java index ca1ccf7066..3851639660 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/AbstractKey.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/AbstractKey.java @@ -40,29 +40,24 @@ public abstract class AbstractKey extends ModelEntity String name; @OneToMany(targetEntity = RoleEntity.class, mappedBy = "key", fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("R") protected Set roles = new LinkedHashSet<>(); @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("PR") @JoinColumn protected AbstractKey register; @OneToMany(targetEntity = AbstractKey.class, mappedBy = "register", fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("RK") protected Set keys = new LinkedHashSet(); @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("PP") @JoinColumn protected AbstractKey parent; @OneToMany(targetEntity = AbstractKey.class, mappedBy = "parent", fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("PK") protected Set otherKeys = new LinkedHashSet(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/Activity.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/Activity.java index 7b8b45a640..6745acc9c9 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/Activity.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/Activity.java @@ -52,7 +52,6 @@ public class Activity extends BaseEntity { } @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("Instruction") @JoinColumn(name = "Instruction_Id") public Instruction getInstruction() { @@ -66,7 +65,6 @@ public class Activity extends BaseEntity { @SuppressWarnings("unused") @ManyToOne(fetch=FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("webApplication") @JoinColumn(name="web_app_oid") public WebApplication getWebApplication() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BatchFetchProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BatchFetchProxyTest.java index 9cefe19f3b..70522bccd7 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BatchFetchProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BatchFetchProxyTest.java @@ -246,7 +246,6 @@ public class BatchFetchProxyTest extends BaseNonConfigCoreFunctionalTestCase { private String name; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @LazyToOne(LazyToOneOption.NO_PROXY) private Employer employer; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BidirectionalProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BidirectionalProxyTest.java index fd91eb9cf1..2ffdf94dbb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BidirectionalProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/BidirectionalProxyTest.java @@ -211,7 +211,6 @@ public class BidirectionalProxyTest extends BaseNonConfigCoreFunctionalTestCase } @ManyToOne(fetch= FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("aEntity") @JoinColumn(name="aEntity") protected AEntity aChildEntity = null; @@ -249,7 +248,6 @@ public class BidirectionalProxyTest extends BaseNonConfigCoreFunctionalTestCase } @ManyToOne(fetch= FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("aEntity") @JoinColumn(name="aEntity") protected AChildEntity aChildEntity = null; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/EntitySharedInCollectionAndToOneTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/EntitySharedInCollectionAndToOneTest.java index 1794cb01fd..0735e8e998 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/EntitySharedInCollectionAndToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/EntitySharedInCollectionAndToOneTest.java @@ -212,7 +212,6 @@ public class EntitySharedInCollectionAndToOneTest extends BaseNonConfigCoreFunct private CodeTable codeTable; @OneToOne( mappedBy = "defaultItem", fetch=FetchType.LAZY ) - @LazyToOne( LazyToOneOption.NO_PROXY ) @LazyGroup( "defaultItemInverse" ) protected CodeTable defaultItemInverse; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/FetchGraphTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/FetchGraphTest.java index 244a728601..e9f7d86e4f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/FetchGraphTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/FetchGraphTest.java @@ -38,6 +38,7 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.HibernateProxy; import org.hibernate.stat.spi.StatisticsImplementor; +import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; @@ -199,6 +200,7 @@ public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @FailureExpected( jiraKey = "HHH-13658") public void testRandomAccess() { final StatisticsImplementor stats = sessionFactory().getStatistics(); stats.clear(); @@ -787,14 +789,10 @@ public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase { // ****** Relations ***************** @OneToOne(fetch = FetchType.LAZY) -// @LazyToOne(LazyToOneOption.PROXY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("a") public AEntity a; @OneToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) -// @LazyToOne(LazyToOneOption.PROXY) @LazyGroup("c") public CEntity c; @@ -802,7 +800,6 @@ public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase { public Set bs; @OneToOne(mappedBy = "d", fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("e") private EEntity e; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatefulTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatefulTest.java index 27564fc8b5..40bf584ac3 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatefulTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatefulTest.java @@ -264,19 +264,15 @@ public class LazyToOnesNoProxyFactoryWithSubclassesStatefulTest extends BaseNonC private String id; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Animal animal = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Primate primate = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Human human = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Human otherHuman = null; protected OtherEntity() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java index 60c97c14f9..cbbbc966e4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java @@ -264,19 +264,15 @@ public class LazyToOnesNoProxyFactoryWithSubclassesStatelessTest extends BaseNon private String id; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Animal animal = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Primate primate = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Human human = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Human otherHuman = null; protected OtherEntity() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyMergeWithSubclassesTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyMergeWithSubclassesTest.java index e4342c5424..3ee27bbf8f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyMergeWithSubclassesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyMergeWithSubclassesTest.java @@ -831,15 +831,12 @@ public class LazyToOnesProxyMergeWithSubclassesTest extends BaseNonConfigCoreFun private String id; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @LazyToOne(LazyToOneOption.NO_PROXY) private Animal animal = null; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @LazyToOne(LazyToOneOption.NO_PROXY) private Primate primate = null; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @LazyToOne(LazyToOneOption.NO_PROXY) private Human human = null; protected OtherEntity() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java index 28b03e2301..5eb5a38aac 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java @@ -298,15 +298,12 @@ public class LazyToOnesProxyWithSubclassesStatelessTest extends BaseNonConfigCor private String id; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Animal animal = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Primate primate = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Human human = null; protected OtherEntity() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java index 0f853fdd06..47a8925fce 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java @@ -366,15 +366,12 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction private String id; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Animal animal = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Primate primate = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Human human = null; protected OtherEntity() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingEntityTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingEntityTest.java index 639827c46b..c7816936e4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingEntityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingEntityTest.java @@ -180,7 +180,6 @@ public class LoadANonExistingEntityTest extends BaseNonConfigCoreFunctionalTestC private String name; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @LazyToOne(LazyToOneOption.NO_PROXY) private Employer employer; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundBatchEntityTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundBatchEntityTest.java index 94b155aa5d..32561ea383 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundBatchEntityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundBatchEntityTest.java @@ -203,7 +203,6 @@ public class LoadANonExistingNotFoundBatchEntityTest extends BaseNonConfigCoreFu @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(foreignKey = @ForeignKey(value= ConstraintMode.NO_CONSTRAINT)) - @LazyToOne(LazyToOneOption.NO_PROXY) @NotFound(action=NotFoundAction.IGNORE) private Employer employer; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundEntityTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundEntityTest.java index f74dc00938..f193ffe4eb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundEntityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LoadANonExistingNotFoundEntityTest.java @@ -183,7 +183,6 @@ public class LoadANonExistingNotFoundEntityTest extends BaseNonConfigCoreFunctio @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(foreignKey = @ForeignKey(value= ConstraintMode.NO_CONSTRAINT)) - @LazyToOne(LazyToOneOption.NO_PROXY) @NotFound(action=NotFoundAction.IGNORE) private Employer employer; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyBidirectionalTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyBidirectionalTest.java index 39c0900f42..9488550aef 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyBidirectionalTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyBidirectionalTest.java @@ -153,7 +153,6 @@ public class MapsIdProxyBidirectionalTest extends BaseNonConfigCoreFunctionalTes @MapsId @OneToOne(optional = false, fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Employer employer; private String info; @@ -165,7 +164,6 @@ public class MapsIdProxyBidirectionalTest extends BaseNonConfigCoreFunctionalTes private int id; @OneToOne(optional = false, fetch = FetchType.LAZY, mappedBy = "employer", cascade = CascadeType.ALL) - @LazyToOne(LazyToOneOption.NO_PROXY) private EmployerInfo employerInfo; private String name; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyUnidirectionalTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyUnidirectionalTest.java index 8fb188f4a0..0d763ac02b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyUnidirectionalTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MapsIdProxyUnidirectionalTest.java @@ -122,7 +122,6 @@ public class MapsIdProxyUnidirectionalTest extends BaseNonConfigCoreFunctionalTe @MapsId @OneToOne(optional = false, fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private Employer employer; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MergeDetachedToProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MergeDetachedToProxyTest.java index 953bde61dd..c05c93eeec 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MergeDetachedToProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MergeDetachedToProxyTest.java @@ -177,7 +177,6 @@ public class MergeDetachedToProxyTest extends BaseNonConfigCoreFunctionalTestCas private int id; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) private BEntity bEntity; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyEagerManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyEagerManyToOneTest.java index 0e17c49fe6..560a607ed3 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyEagerManyToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyEagerManyToOneTest.java @@ -302,7 +302,6 @@ public class QueryScrollingWithInheritanceProxyEagerManyToOneTest extends BaseNo private String id; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @JoinColumn(name = "Employee_Id") protected Employee employee = null; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyTest.java index d0e93b67e5..c756a89f04 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/QueryScrollingWithInheritanceProxyTest.java @@ -302,12 +302,10 @@ public class QueryScrollingWithInheritanceProxyTest extends BaseNonConfigCoreFun private String id; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @JoinColumn(name = "Employee_Id") protected Employee employee = null; @ManyToOne(fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @JoinColumn(name = "EmployeeParent_Id") protected EmployeeParent employeeParent = null; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java index fa896483fd..b8faa119d9 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java @@ -283,7 +283,6 @@ public class SimpleUpdateTestWithLazyLoading extends BaseNonConfigCoreFunctional Long id; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) Parent parent; @OneToMany diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java index 3b0de35da4..c2f0ad46cf 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java @@ -235,7 +235,6 @@ public class SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking extends BaseN Long id; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) Parent parent; @OneToMany diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/WebApplication.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/WebApplication.java index d25377109c..72ac8889b6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/WebApplication.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/WebApplication.java @@ -59,7 +59,6 @@ public class WebApplication extends BaseEntity { } @OneToMany(mappedBy="webApplication", fetch= FetchType.LAZY) - @LazyToOne(LazyToOneOption.NO_PROXY) @LazyGroup("app_activity_group") // @CollectionType(type="baseutil.technology.hibernate.IskvLinkedSetCollectionType") public Set getActivities() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mutable/TestEntity.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mutable/TestEntity.java index 3947045b6f..cb99d834b1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mutable/TestEntity.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mutable/TestEntity.java @@ -23,6 +23,7 @@ public class TestEntity { private Long id; @Temporal(TemporalType.TIMESTAMP) + @Column(name = "t_date") private Date date; @Basic diff --git a/hibernate-core/src/test/java/org/hibernate/test/foreignkeys/disabled/OneToManyBidirectionalForeignKeyTest.java b/hibernate-core/src/test/java/org/hibernate/test/foreignkeys/disabled/OneToManyBidirectionalForeignKeyTest.java new file mode 100644 index 0000000000..38cd379156 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/foreignkeys/disabled/OneToManyBidirectionalForeignKeyTest.java @@ -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 . + */ +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 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 children = new ArrayList<>(0); + + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/join/OuterJoinTest.java b/hibernate-core/src/test/java/org/hibernate/test/join/OuterJoinTest.java new file mode 100644 index 0000000000..e4da3b85c8 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/join/OuterJoinTest.java @@ -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 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 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 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 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 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 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 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 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)); + }); + } + +} diff --git a/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/Bean.java b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/Bean.java new file mode 100644 index 0000000000..5049283aa7 --- /dev/null +++ b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/Bean.java @@ -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 . + */ +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 ); + } +} diff --git a/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/BeanReflectionHelper.java b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/BeanReflectionHelper.java new file mode 100644 index 0000000000..a771152409 --- /dev/null +++ b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/BeanReflectionHelper.java @@ -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 . + */ +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; + } +} diff --git a/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/BulkAccessorTest.java b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/BulkAccessorTest.java new file mode 100644 index 0000000000..a513e6d925 --- /dev/null +++ b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/javassist/BulkAccessorTest.java @@ -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 . + */ +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() + ); + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java index 8f68358a81..f0ab2ad6f2 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java @@ -9,11 +9,14 @@ package org.hibernate.envers.boot.internal; import java.util.Map; import java.util.Properties; +import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.cfg.AvailableSettings; 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.EntitiesConfigurator; import org.hibernate.envers.configuration.internal.GlobalConfiguration; @@ -36,6 +39,8 @@ import org.hibernate.service.spi.Stoppable; import org.jboss.logging.Logger; +import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED; + /** * 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 ); + 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 ); } diff --git a/hibernate-micrometer/hibernate-micrometer.gradle b/hibernate-micrometer/hibernate-micrometer.gradle new file mode 100644 index 0000000000..dccd513b68 --- /dev/null +++ b/hibernate-micrometer/hibernate-micrometer.gradle @@ -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 + } + } +} diff --git a/hibernate-micrometer/src/main/java/org/hibernate/stat/HibernateMetrics.java b/hibernate-micrometer/src/main/java/org/hibernate/stat/HibernateMetrics.java new file mode 100644 index 0000000000..339fc6d159 --- /dev/null +++ b/hibernate-micrometer/src/main/java/org/hibernate/stat/HibernateMetrics.java @@ -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 . + */ +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 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 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 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 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; + } + } + +} diff --git a/hibernate-micrometer/src/main/java/org/hibernate/stat/HibernateQueryMetrics.java b/hibernate-micrometer/src/main/java/org/hibernate/stat/HibernateQueryMetrics.java new file mode 100644 index 0000000000..21f9687cb0 --- /dev/null +++ b/hibernate-micrometer/src/main/java/org/hibernate/stat/HibernateQueryMetrics.java @@ -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 . + */ +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}. + *

+ * 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 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 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 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 ); + } + } + } +} diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/Account.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/Account.java new file mode 100644 index 0000000000..0cc4ea1eb3 --- /dev/null +++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/Account.java @@ -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 . + */ +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; + } +} diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/AccountId.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/AccountId.java new file mode 100644 index 0000000000..1112721949 --- /dev/null +++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/AccountId.java @@ -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 . + */ +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; + } +} + diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerCacheStatisticsTest.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerCacheStatisticsTest.java new file mode 100644 index 0000000000..0430e3db28 --- /dev/null +++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerCacheStatisticsTest.java @@ -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 . + */ +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 nickNames; + } +} diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerStatisticsTest.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerStatisticsTest.java new file mode 100644 index 0000000000..43965841e1 --- /dev/null +++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerStatisticsTest.java @@ -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 . + */ +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) { + } + } +} diff --git a/hibernate-micrometer/src/test/resources/hibernate.properties b/hibernate-micrometer/src/test/resources/hibernate.properties new file mode 100644 index 0000000000..de12583ef4 --- /dev/null +++ b/hibernate-micrometer/src/test/resources/hibernate.properties @@ -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 . +# + +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 \ No newline at end of file diff --git a/hibernate-micrometer/src/test/resources/log4j.properties b/hibernate-micrometer/src/test/resources/log4j.properties new file mode 100644 index 0000000000..4567cb1105 --- /dev/null +++ b/hibernate-micrometer/src/test/resources/log4j.properties @@ -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 . +# +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 \ No newline at end of file diff --git a/hibernate-proxool/hibernate-proxool.gradle b/hibernate-proxool/hibernate-proxool.gradle index 8aa0fb69c6..56ace9a389 100644 --- a/hibernate-proxool/hibernate-proxool.gradle +++ b/hibernate-proxool/hibernate-proxool.gradle @@ -15,3 +15,15 @@ dependencies { compile( libraries.proxool ) 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.(Enhancer.java:96) + jvmArgs( ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] ) + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index b2a10652a5..e776fba9dd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -106,7 +106,7 @@ include 'hibernate-agroal' include 'hibernate-jcache' include 'hibernate-ehcache' include 'hibernate-infinispan' - +include 'hibernate-micrometer' include 'hibernate-graalvm' // The plugin used to generate Java modules was compiled using JDK11. diff --git a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle index 338c5891ea..e01636c6f0 100644 --- a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle +++ b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle @@ -134,3 +134,9 @@ else { " 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'] ) + } +}