diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 0fe2a23e5b..48f95889fb 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -16,15 +16,44 @@ apply plugin: 'hibernate-matrix-testing' description = 'Hibernate\'s core ORM functionality' +ext { + jaxbTargetDir = file( "${buildDir}/generated-src/jaxb/main" ) +} + +sourceSets.main { + java.srcDir project.jaxbTargetDir +} + +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 + } + } +} + configurations { tests { description = 'Configuration for the produced test jar' } + + //Configures the compile and runtime configurations for our javassist tests + //and includes the dependencies of the test task. + testJavassistCompile.extendsFrom testCompile + testJavassistRuntime.extendsFrom testRuntime } dependencies { compile( libraries.jpa ) - // Javassist is no longer the default enhancer but still required for other purposes, e.g. Scanning + // This can now be made provided compile( libraries.javassist ) compile( libraries.byteBuddy ) compile( libraries.antlr ) @@ -107,6 +136,10 @@ dependencies { testCompile libraries.jboss_ejb_spec_jar testCompile libraries.jboss_annotation_spec_jar + + // Additional tests requiring Javassist + // folder in src/javassist/java + testJavassistCompile libraries.javassist } jar { @@ -407,4 +440,18 @@ class Antlr4GenerationTask extends DefaultTask { args sourceFile.absolutePath } } -} \ No newline at end of file + +} +//Create the task that runs the integration tests found from the +//configured source directory and uses the correct classpath. +task testJavassist(type: Test) { + testClassesDirs = sourceSets.testJavassist.output.classesDirs + classpath = sourceSets.testJavassist.runtimeClasspath + //If you want to ensure that integration tests are run every time when you invoke + //this task, uncomment the following line. + //outputs.upToDateWhen { false } +} + +check.dependsOn testJavassist +testJavassist.mustRunAfter test + diff --git a/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java b/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java index b005eaf539..7647c0a1cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java @@ -14,9 +14,6 @@ import javax.persistence.Embeddable; import javax.persistence.Entity; import javax.persistence.MappedSuperclass; -import javassist.bytecode.AnnotationsAttribute; -import javassist.bytecode.ClassFile; - import org.hibernate.boot.archive.scan.internal.ClassDescriptorImpl; import org.hibernate.boot.archive.scan.internal.ScanResultCollector; import org.hibernate.boot.archive.spi.ArchiveContext; @@ -24,12 +21,26 @@ import org.hibernate.boot.archive.spi.ArchiveEntry; import org.hibernate.boot.archive.spi.ArchiveEntryHandler; import org.hibernate.boot.archive.spi.ArchiveException; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; +import org.jboss.jandex.Indexer; + /** * Defines handling and filtering for class file entries within an archive * * @author Steve Ebersole */ public class ClassFileArchiveEntryHandler implements ArchiveEntryHandler { + + private final static DotName CONVERTER = DotName.createSimple( Converter.class.getName() ); + + private final static DotName[] MODELS = { + DotName.createSimple( Entity.class.getName() ), + DotName.createSimple( MappedSuperclass.class.getName() ), + DotName.createSimple( Embeddable.class.getName() ) + }; + private final ScanResultCollector resultCollector; public ClassFileArchiveEntryHandler(ScanResultCollector resultCollector) { @@ -38,14 +49,8 @@ public class ClassFileArchiveEntryHandler implements ArchiveEntryHandler { @Override public void handleEntry(ArchiveEntry entry, ArchiveContext context) { - // Ultimately we'd like to leverage Jandex here as long term we want to move to - // using Jandex for annotation processing. But even then, Jandex atm does not have - // any facility for passing a stream and conditionally indexing it into an Index or - // returning existing ClassInfo objects. - // - // So not sure we can ever not do this unconditional input stream read :( - final ClassFile classFile = toClassFile( entry ); - final ClassDescriptor classDescriptor = toClassDescriptor( classFile, entry ); + + final ClassDescriptor classDescriptor = toClassDescriptor( entry ); if ( classDescriptor.getCategorization() == ClassDescriptor.Categorization.OTHER ) { return; @@ -54,45 +59,41 @@ public class ClassFileArchiveEntryHandler implements ArchiveEntryHandler { resultCollector.handleClass( classDescriptor, context.isRootUrl() ); } - private ClassFile toClassFile(ArchiveEntry entry) { - final InputStream inputStream = entry.getStreamAccess().accessInputStream(); - final DataInputStream dataInputStream = new DataInputStream( inputStream ); - try { - return new ClassFile( dataInputStream ); + private ClassDescriptor toClassDescriptor(ArchiveEntry entry) { + try (InputStream inputStream = entry.getStreamAccess().accessInputStream()) { + Indexer indexer = new Indexer(); + ClassInfo classInfo = indexer.index( inputStream ); + Index index = indexer.complete(); + return toClassDescriptor( classInfo, index, entry ); } catch (IOException e) { - throw new ArchiveException( "Could not build ClassFile", e ); - } - finally { - try { - dataInputStream.close(); - } - catch (Exception ignore) { - } - - try { - inputStream.close(); - } - catch (IOException ignore) { - } + throw new ArchiveException( "Could not build ClassInfo", e ); } } - private ClassDescriptor toClassDescriptor(ClassFile classFile, ArchiveEntry entry) { + private ClassDescriptor toClassDescriptor(ClassInfo classInfo, Index index, ArchiveEntry entry) { ClassDescriptor.Categorization categorization = ClassDescriptor.Categorization.OTHER; - final AnnotationsAttribute visibleAnnotations = (AnnotationsAttribute) classFile.getAttribute( AnnotationsAttribute.visibleTag ); - if ( visibleAnnotations != null ) { - if ( visibleAnnotations.getAnnotation( Entity.class.getName() ) != null - || visibleAnnotations.getAnnotation( MappedSuperclass.class.getName() ) != null - || visibleAnnotations.getAnnotation( Embeddable.class.getName() ) != null ) { - categorization = ClassDescriptor.Categorization.MODEL; - } - else if ( visibleAnnotations.getAnnotation( Converter.class.getName() ) != null ) { - categorization = ClassDescriptor.Categorization.CONVERTER; - } + if ( isModel( index ) ) { + categorization = ClassDescriptor.Categorization.MODEL; + } + else if ( isConverter( index ) ) { + categorization = ClassDescriptor.Categorization.CONVERTER; } - return new ClassDescriptorImpl( classFile.getName(), categorization, entry.getStreamAccess() ); + return new ClassDescriptorImpl( classInfo.name().toString(), categorization, entry.getStreamAccess() ); + } + + private boolean isConverter(Index index) { + return !index.getAnnotations( CONVERTER ).isEmpty(); + } + + private boolean isModel(Index index) { + for ( DotName model : MODELS ) { + if ( !index.getAnnotations( model ).isEmpty() ) { + return true; + } + } + return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java index 2c870b1844..7b846dceab 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java @@ -26,6 +26,14 @@ import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.member.MethodInvocation; +import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; +import net.bytebuddy.jar.asm.Opcodes; class CodeTemplates { @@ -510,4 +518,26 @@ class CodeTemplates { @interface MappedBy { } + + // mapping to get private field from superclass by calling the enhanced reader, for use when field is not visible + static class GetterMapping implements Advice.OffsetMapping { + + private final FieldDescription persistentField; + + GetterMapping(FieldDescription persistentField) { + this.persistentField = persistentField; + } + + @Override public Target resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler, Sort sort) { + MethodDescription.Token signature = new MethodDescription.Token( EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName(), Opcodes.ACC_PUBLIC, persistentField.getType() ); + MethodDescription method = new MethodDescription.Latent( instrumentedType.getSuperClass().asErasure(), signature ); + + return new Target.AbstractReadOnlyAdapter() { + @Override + public StackManipulation resolveRead() { + return new StackManipulation.Compound( MethodVariableAccess.loadThis(), MethodInvocation.invoke( method ).special( method.getDeclaringType().asErasure() ) ); + } + }; + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java index 56dbb9d54b..11d0506e00 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java @@ -285,7 +285,7 @@ public class EnhancerImpl implements Enhancer { } } - return createTransformer( managedCtClass ).applyTo( builder, false ); + return createTransformer( managedCtClass ).applyTo( builder ); } else if ( enhancementContext.isCompositeClass( managedCtClass ) ) { log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() ); @@ -318,13 +318,13 @@ public class EnhancerImpl implements Enhancer { .intercept( implementationClearOwner ); } - return createTransformer( managedCtClass ).applyTo( builder, false ); + return createTransformer( managedCtClass ).applyTo( builder ); } else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() ); builder = builder.implement( ManagedMappedSuperclass.class ); - return createTransformer( managedCtClass ).applyTo( builder, true ); + return createTransformer( managedCtClass ).applyTo( builder ); } else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) { log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java index 34e2bb30cf..1c61641c29 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java @@ -64,8 +64,15 @@ final class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppend if ( enhancementContext.isCompositeClass( persistentField.getType().asErasure() ) && persistentField.hasAnnotation( Embedded.class ) ) { - implementation = Advice.withCustomMapping() - .bind( CodeTemplates.FieldValue.class, persistentField.getFieldDescription() ) + // HHH-13759 - Call getter on superclass if field is not visible + // An embedded field won't be visible if declared private in a superclass + // annotated with @MappedSuperclass + Advice.WithCustomMapping advice = Advice.withCustomMapping(); + advice = persistentField.isVisibleTo( managedCtClass ) + ? advice.bind( CodeTemplates.FieldValue.class, persistentField.getFieldDescription() ) + : advice.bind( CodeTemplates.FieldValue.class, new CodeTemplates.GetterMapping( persistentField.getFieldDescription() ) ); + + implementation = advice .bind( CodeTemplates.FieldName.class, persistentField.getName() ) .to( CodeTemplates.CompositeFieldDirtyCheckingHandler.class ) .wrap( implementation ); diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java index 3b9eb4114b..e482e6f7cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java @@ -175,7 +175,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla return false; } - DynamicType.Builder applyTo(DynamicType.Builder builder, boolean accessor) { + DynamicType.Builder applyTo(DynamicType.Builder builder) { boolean compositeOwner = false; builder = builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, this ) ); @@ -186,10 +186,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla enhancedField.getType().asErasure(), Visibility.PUBLIC ) - .intercept( - accessor - ? FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() ) - : fieldReader( enhancedField ) + .intercept( fieldReader( enhancedField ) ) .defineMethod( EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + enhancedField.getName(), @@ -197,12 +194,10 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla Visibility.PUBLIC ) .withParameters( enhancedField.getType().asErasure() ) - .intercept( accessor - ? FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() ) - : fieldWriter( enhancedField ) ); + .intercept( fieldWriter( enhancedField ) ); if ( !compositeOwner - && !accessor + && !enhancementContext.isMappedSuperclassClass( managedCtClass ) && enhancedField.hasAnnotation( Embedded.class ) && enhancementContext.isCompositeClass( enhancedField.getType().asErasure() ) && enhancementContext.doDirtyCheckingInline( managedCtClass ) ) { @@ -228,6 +223,9 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla } private Implementation fieldReader(AnnotatedFieldDescription enhancedField) { + if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { + return FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() ); + } if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( enhancedField ) ) { if ( enhancedField.getDeclaringType().asErasure().equals( managedCtClass ) ) { return FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() ); @@ -242,20 +240,29 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla } private Implementation fieldWriter(AnnotatedFieldDescription enhancedField) { - Implementation implementation; + Implementation implementation = fieldWriterImplementation( enhancedField ); + if ( !enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { + implementation = InlineDirtyCheckingHandler.wrap( managedCtClass, enhancementContext, enhancedField, implementation ); + implementation = BiDirectionalAssociationHandler.wrap( managedCtClass, enhancementContext, enhancedField, implementation ); + } + return implementation; + } + + private Implementation fieldWriterImplementation(AnnotatedFieldDescription enhancedField) { + if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { + return FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() ); + } if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( enhancedField ) ) { if ( enhancedField.getDeclaringType().asErasure().equals( managedCtClass ) ) { - implementation = FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() ); + return FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() ); } else { - implementation = new Implementation.Simple( new FieldMethodWriter( managedCtClass, enhancedField ) ); + return new Implementation.Simple( new FieldMethodWriter( managedCtClass, enhancedField ) ); } } else { - implementation = new Implementation.Simple( FieldWriterAppender.of( managedCtClass, enhancedField ) ); + return new Implementation.Simple( FieldWriterAppender.of( managedCtClass, enhancedField ) ); } - implementation = InlineDirtyCheckingHandler.wrap( managedCtClass, enhancementContext, enhancedField, implementation ); - return BiDirectionalAssociationHandler.wrap( managedCtClass, enhancementContext, enhancedField, implementation ); } DynamicType.Builder applyExtended(DynamicType.Builder builder) { diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/javassist/PersistentAttributesEnhancer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/javassist/PersistentAttributesEnhancer.java index 990553fead..17e70f2416 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/javassist/PersistentAttributesEnhancer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/javassist/PersistentAttributesEnhancer.java @@ -520,26 +520,30 @@ public class PersistentAttributesEnhancer extends EnhancerImpl { // make sure to add the CompositeOwner interface addCompositeOwnerInterface( managedCtClass ); + String readFragment = persistentField.visibleFrom( managedCtClass ) ? persistentField.getName() : "super." + EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName() + "()"; + // cleanup previous owner fieldWriter.insertBefore( String.format( - "if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n", - persistentField.getName(), + "if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%4$s\"); }%n", + readFragment, CompositeTracker.class.getName(), - EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER + EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER, + persistentField.getName() ) ); // trigger track changes fieldWriter.insertAfter( String.format( - "if (%1$s != null) { ((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this); }%n" + - "%5$s(\"%1$s\");", - persistentField.getName(), + "if (%1$s != null) { ((%2$s) %1$s).%4$s(\"%6$s\", (%3$s) this); }%n" + + "%5$s(\"%6$s\");", + readFragment, CompositeTracker.class.getName(), CompositeOwner.class.getName(), EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER, - EnhancerConstants.TRACKER_CHANGER_NAME + EnhancerConstants.TRACKER_CHANGER_NAME, + persistentField.getName() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/spi/ProxyFactoryFactory.java b/hibernate-core/src/main/java/org/hibernate/bytecode/spi/ProxyFactoryFactory.java index 2565bf35a4..7e624c0145 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/spi/ProxyFactoryFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/spi/ProxyFactoryFactory.java @@ -15,7 +15,7 @@ import org.hibernate.service.ServiceRegistry; /** * An interface for factories of {@link ProxyFactory proxy factory} instances. *

- * Currently used to abstract from the tupizer whether we are using Byte Buddy or + * Currently used to abstract from the tuplizer whether we are using Byte Buddy or * Javassist for lazy proxy generation. * * @author Steve Ebersole diff --git a/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java index 47a75e157b..b50024de9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java @@ -10,6 +10,7 @@ import java.util.Properties; import org.hibernate.MappingException; import org.hibernate.Session; +import org.hibernate.StatelessSession; import org.hibernate.TransientObjectException; import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -78,9 +79,6 @@ public class ForeignGenerator implements IdentifierGenerator, Configurable { @Override public Object generate(SharedSessionContractImplementor sessionImplementor, Object object) { - // needs to be a Session for the #save and #contains calls below... - final Session session = ( Session ) sessionImplementor; - final EntityPersister persister = sessionImplementor.getFactory().getMetamodel().entityPersister( entityName ); Object associatedObject = persister.getPropertyValue( object, propertyName ); if ( associatedObject == null ) { @@ -115,10 +113,20 @@ public class ForeignGenerator implements IdentifierGenerator, Configurable { foreignValueSourceType.getAssociatedEntityName() ); } - id = session.save( foreignValueSourceType.getAssociatedEntityName(), associatedObject ); + if (sessionImplementor instanceof Session) { + id = ((Session) sessionImplementor) + .save(foreignValueSourceType.getAssociatedEntityName(), associatedObject); + } + else if (sessionImplementor instanceof StatelessSession) { + id = ((StatelessSession) sessionImplementor) + .insert(foreignValueSourceType.getAssociatedEntityName(), associatedObject); + } + else { + throw new IdentifierGenerationException("sessionImplementor is neither Session nor StatelessSession"); + } } - if ( session.contains( entityName, object ) ) { + if ( sessionImplementor instanceof Session && ((Session) sessionImplementor).contains( entityName, object ) ) { //abort the save (the object is already saved by a circular cascade) return IdentifierGeneratorHelper.SHORT_CIRCUIT_INDICATOR; //throw new IdentifierGenerationException("save associated object first, or disable cascade for inverse association"); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java index a6296f0211..233943cc57 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java @@ -14,6 +14,8 @@ import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Stream; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; import javax.persistence.NoResultException; @@ -902,4 +904,11 @@ public class ProcedureCallImpl public ProcedureCallImplementor setParameter(int position, Date value, TemporalType temporalPrecision) { return (ProcedureCallImplementor) super.setParameter( position, value, temporalPrecision ); } + + @Override + @SuppressWarnings("unchecked") + public Stream getResultStream() { + return getResultList().stream(); + } + } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhance/internal/bytebuddy/DirtyCheckingWithEmbeddableAndMappedSuperclassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhance/internal/bytebuddy/DirtyCheckingWithEmbeddableAndMappedSuperclassTest.java new file mode 100644 index 0000000000..cab0230a0b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhance/internal/bytebuddy/DirtyCheckingWithEmbeddableAndMappedSuperclassTest.java @@ -0,0 +1,210 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.bytecode.enhance.internal.bytebuddy; + +import java.lang.reflect.Method; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker; +import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_ENTRY_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_ENTRY_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_SETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_SETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_CLEAR_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_GET_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_HAS_CHANGED_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_SUSPEND_NAME; + +@TestForIssue(jiraKey = "HHH-13759") +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(inlineDirtyChecking = true) +public class DirtyCheckingWithEmbeddableAndMappedSuperclassTest { + + @Test + public void shouldDeclareFieldsInEntityClass() { + assertThat( CardGame.class ) + .hasDeclaredFields( ENTITY_ENTRY_FIELD_NAME, PREVIOUS_FIELD_NAME, NEXT_FIELD_NAME, TRACKER_FIELD_NAME ); + } + + @Test + public void shouldDeclareMethodsInEntityClass() { + assertThat( CardGame.class ) + .hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "id", PERSISTENT_FIELD_WRITER_PREFIX + "id" ) + .hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "name", PERSISTENT_FIELD_WRITER_PREFIX + "name" ) + .hasDeclaredMethods( ENTITY_INSTANCE_GETTER_NAME, ENTITY_ENTRY_GETTER_NAME ) + .hasDeclaredMethods( PREVIOUS_GETTER_NAME, PREVIOUS_SETTER_NAME, NEXT_GETTER_NAME, NEXT_SETTER_NAME ) + .hasDeclaredMethods( TRACKER_HAS_CHANGED_NAME, TRACKER_CLEAR_NAME, TRACKER_SUSPEND_NAME, TRACKER_GET_NAME ); + } + + @Test + public void shouldDeclareFieldsInEmbeddedClass() { + assertThat( Component.class ) + .hasDeclaredFields( TRACKER_COMPOSITE_FIELD_NAME ); + } + + @Test + public void shouldDeclareMethodsInEmbeddedClass() { + assertThat(Component.class ) + .hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "component", PERSISTENT_FIELD_WRITER_PREFIX + "component" ) + .hasDeclaredMethods( TRACKER_COMPOSITE_SET_OWNER, TRACKER_COMPOSITE_CLEAR_OWNER ); + } + + @Test + public void shouldCreateTheTracker() throws Exception { + CardGame entity = new CardGame( "MTG", "Magic the Gathering" ); + assertThat( entity ) + .extracting( NEXT_FIELD_NAME ).isNull(); + assertThat( entity ) + .extracting( PREVIOUS_FIELD_NAME ).isNull(); + assertThat( entity ) + .extracting( ENTITY_ENTRY_FIELD_NAME ).isNull(); + assertThat( entity ) + .extracting( TRACKER_FIELD_NAME ).isInstanceOf( SimpleFieldTracker.class ); + assertThat( entity.getFirstPlayerToken() ) + .extracting( TRACKER_COMPOSITE_FIELD_NAME ).isInstanceOf( CompositeOwnerTracker.class); + + assertThat( entity ).extracting( TRACKER_HAS_CHANGED_NAME ).isEqualTo( true ); + assertThat( entity ).extracting( TRACKER_GET_NAME ) + .isEqualTo( new String[] { "name", "firstPlayerToken" } ); + assertThat( entity.getFirstPlayerToken() ) + .extracting( TRACKER_COMPOSITE_FIELD_NAME + ".names" ).isEqualTo( new String[] { "firstPlayerToken" } ); + } + + @Test + public void shouldResetTheTracker() throws Exception { + CardGame entity = new CardGame( "7WD", "7 Wonders duel" ); + + Method trackerClearMethod = CardGame.class.getMethod( TRACKER_CLEAR_NAME ); + trackerClearMethod.invoke( entity ); + + assertThat( entity ).extracting( TRACKER_HAS_CHANGED_NAME ).isEqualTo( false ); + assertThat( entity ).extracting( TRACKER_GET_NAME ).isEqualTo( new String[0] ); + } + + @Test + public void shouldUpdateTheTracker() throws Exception { + CardGame entity = new CardGame( "SPL", "Splendor" ); + + Method trackerClearMethod = CardGame.class.getMethod( TRACKER_CLEAR_NAME ); + trackerClearMethod.invoke( entity ); + + entity.setName( "Splendor: Cities of Splendor" ); + + assertThat( entity ).extracting( TRACKER_HAS_CHANGED_NAME ).isEqualTo( true ); + assertThat( entity ).extracting( TRACKER_GET_NAME ) + .isEqualTo( new String[] { "name", "firstPlayerToken" } ); + + trackerClearMethod.invoke( entity ); + + entity.setFirstPlayerToken( new Component( "FIRST PLAYER!!!!!!!!" ) ); + assertThat( entity ).extracting( TRACKER_GET_NAME ) + .isEqualTo( new String[] { "firstPlayerToken" } ); + assertThat( entity.getFirstPlayerToken() ) + .extracting( TRACKER_COMPOSITE_FIELD_NAME + ".names" ).isEqualTo( new String[] { "firstPlayerToken" } ); + } + + @MappedSuperclass + public static abstract class TableTopGame { + + @Embedded + private Component firstPlayerToken; + + public Component getFirstPlayerToken() { + return firstPlayerToken; + } + + public void setFirstPlayerToken(Component firstPlayerToken) { + this.firstPlayerToken = firstPlayerToken; + } + } + + @Embeddable + public static class Component { + + @Column(name = "first_player_token") + private String component; + + public Component() { + } + + private Component(String component) { + this.component = component; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + } + + @Entity(name = "CardGame") + public static class CardGame extends TableTopGame { + + @Id + private String id; + private String name; + + public CardGame() { + } + + private CardGame(String id, String name) { + this.id = id; + this.name = name; + setFirstPlayerToken( createEmbeddedValue( name ) ); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + setFirstPlayerToken( createEmbeddedValue( name ) ); + } + + private Component createEmbeddedValue(String name) { + return new Component( name + " first player token"); + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhance/internal/bytebuddy/DirtyCheckingWithMappedsuperclassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhance/internal/bytebuddy/DirtyCheckingWithMappedsuperclassTest.java new file mode 100644 index 0000000000..3b0fea279a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhance/internal/bytebuddy/DirtyCheckingWithMappedsuperclassTest.java @@ -0,0 +1,162 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.bytecode.enhance.internal.bytebuddy; + +import java.lang.reflect.Method; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_ENTRY_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_ENTRY_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_SETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_GETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_SETTER_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_CLEAR_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_FIELD_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_GET_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_HAS_CHANGED_NAME; +import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_SUSPEND_NAME; + +@TestForIssue(jiraKey = "HHH-13759") +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(inlineDirtyChecking = true) +public class DirtyCheckingWithMappedsuperclassTest { + + @Test + public void shouldDeclareFieldsInEntityClass() { + assertThat( CardGame.class ) + .hasDeclaredFields( ENTITY_ENTRY_FIELD_NAME, PREVIOUS_FIELD_NAME, NEXT_FIELD_NAME, TRACKER_FIELD_NAME ); + } + + @Test + public void shouldDeclareMethodsInEntityClass() { + assertThat( CardGame.class ) + .hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "id", PERSISTENT_FIELD_WRITER_PREFIX + "id" ) + .hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "name", PERSISTENT_FIELD_WRITER_PREFIX + "name" ) + .hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "code", PERSISTENT_FIELD_WRITER_PREFIX + "code" ) + .hasDeclaredMethods( ENTITY_INSTANCE_GETTER_NAME, ENTITY_ENTRY_GETTER_NAME ) + .hasDeclaredMethods( PREVIOUS_GETTER_NAME, PREVIOUS_SETTER_NAME, NEXT_GETTER_NAME, NEXT_SETTER_NAME ) + .hasDeclaredMethods( TRACKER_HAS_CHANGED_NAME, TRACKER_CLEAR_NAME, TRACKER_SUSPEND_NAME, TRACKER_GET_NAME ); + } + + @Test + public void shouldCreateTheTracker() throws Exception { + CardGame entity = new CardGame( "MTG", "Magic the Gathering" ); + assertThat( entity ) + .extracting( NEXT_FIELD_NAME ).isNull(); + assertThat( entity ) + .extracting( PREVIOUS_FIELD_NAME ).isNull(); + assertThat( entity ) + .extracting( ENTITY_ENTRY_FIELD_NAME ).isNull(); + assertThat( entity ) + .extracting( TRACKER_FIELD_NAME ).isInstanceOf( SimpleFieldTracker.class ); + + assertThat( entity ).extracting( TRACKER_HAS_CHANGED_NAME ).isEqualTo( true ); + assertThat( entity ).extracting( TRACKER_GET_NAME ).isEqualTo( new String[] { "name", "code" } ); + } + + @Test + public void shouldResetTheTracker() throws Exception { + CardGame entity = new CardGame( "7WD", "7 Wonders duel" ); + + Method trackerClearMethod = CardGame.class.getMethod( TRACKER_CLEAR_NAME ); + trackerClearMethod.invoke( entity ); + + assertThat( entity ).extracting( TRACKER_HAS_CHANGED_NAME ).isEqualTo( false ); + assertThat( entity ).extracting( TRACKER_GET_NAME ).isEqualTo( new String[0] ); + } + + @Test + public void shouldUpdateTheTracker() throws Exception { + CardGame entity = new CardGame( "SPL", "Splendor" ); + assertThat( entity.getCode() ).isEqualTo( "XsplX" ); + + Method trackerClearMethod = CardGame.class.getMethod( TRACKER_CLEAR_NAME ); + trackerClearMethod.invoke( entity ); + + entity.setName( "Splendor: Cities of Splendor" ); + + assertThat( entity.getCode() ) + .as( "Field 'code' should have not change" ).isEqualTo( "XsplX" ); + assertThat( entity ).extracting( TRACKER_HAS_CHANGED_NAME ).isEqualTo( true ); + assertThat( entity ).extracting( TRACKER_GET_NAME ).isEqualTo( new String[] { "name" } ); + + entity.setName( "Cities of Splendor" ); + + assertThat( entity ).extracting( TRACKER_GET_NAME ).isEqualTo( new String[] { "name", "code" } ); + } + + @MappedSuperclass + public static abstract class TableTopGame { + + private String code; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + } + + @Entity(name = "CardGame") + public static class CardGame extends TableTopGame { + @Id + private String id; + + private String name; + + public CardGame() { + } + + private CardGame(String id, String name) { + this.id = id; + this.name = name; + setCode( createCode( name ) ); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + setCode( createCode( name ) ); + } + + private String createCode(String name) { + return "X" + name.substring( 0, 3 ).toLowerCase() + "X"; + } + + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/procedure/internal/ProcedureCallImplTest.java b/hibernate-core/src/test/java/org/hibernate/procedure/internal/ProcedureCallImplTest.java new file mode 100644 index 0000000000..0b89867ee2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/procedure/internal/ProcedureCallImplTest.java @@ -0,0 +1,39 @@ +package org.hibernate.procedure.internal; + +import java.util.stream.Stream; +import javax.persistence.EntityManager; +import javax.persistence.Query; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Nathan Xu + */ +public class ProcedureCallImplTest extends BaseEntityManagerFunctionalTestCase { + + @Test + @TestForIssue( jiraKey = "HHH-13644" ) + @RequiresDialect( H2Dialect.class ) + public void testNoNullPointerExceptionThrown() { + + EntityManager em = getOrCreateEntityManager(); + + em.getTransaction().begin(); + + em.createNativeQuery("CREATE ALIAS GET_RANDOM_VALUE FOR \"java.lang.Math.random\";").executeUpdate(); + + Query query = em.createStoredProcedureQuery("GET_RANDOM_VALUE"); + + Stream stream = query.getResultStream(); + + Assert.assertEquals(1, stream.count()); + + em.getTransaction().commit(); + + em.close(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MappedSuperclassWithEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MappedSuperclassWithEmbeddableTest.java new file mode 100644 index 0000000000..4871d85819 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/MappedSuperclassWithEmbeddableTest.java @@ -0,0 +1,148 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy; + +import java.io.Serializable; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertThat; + +/** + * @author Andrea Boriero + */ +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(lazyLoading = true, inlineDirtyChecking = true) +public class MappedSuperclassWithEmbeddableTest extends BaseNonConfigCoreFunctionalTestCase { + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" ); + ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { TestEntity.class }; + } + + @Before + public void prepare() { + doInHibernate( this::sessionFactory, s -> { + TestEntity testEntity = new TestEntity( "2", "test" ); + s.persist( testEntity ); + } ); + } + + @After + public void tearDown() { + doInHibernate( this::sessionFactory, s -> { + s.createQuery( "delete from TestEntity" ).executeUpdate(); + } ); + } + + @Test + public void testIt() { + doInHibernate( this::sessionFactory, s -> { + TestEntity testEntity = s.get( TestEntity.class, "2" ); + assertThat( testEntity, notNullValue() ); + } ); + } + + @MappedSuperclass + public static abstract class BaseEntity { + @Embedded + private EmbeddedValue superField; + + public EmbeddedValue getSuperField() { + return superField; + } + + public void setSuperField(EmbeddedValue superField) { + this.superField = superField; + } + } + + @Embeddable + public static class EmbeddedValue implements Serializable { + @Column(name = "super_field") + private String superField; + + public EmbeddedValue() { + } + + private EmbeddedValue(String superField) { + this.superField = superField; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + EmbeddedValue that = (EmbeddedValue) o; + return superField.equals( that.superField ); + } + + @Override + public int hashCode() { + return Objects.hash( superField ); + } + } + + @Entity(name = "TestEntity") + public static class TestEntity extends BaseEntity { + @Id + private String id; + private String name; + + public TestEntity() { + } + + private TestEntity(String id, String name) { + this.id = id; + this.name = name; + EmbeddedValue value = new EmbeddedValue( "SUPER " + name ); + setSuperField( value ); + } + + + public String id() { + return id; + } + + public String name() { + return name; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/id/ForeignGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/test/id/ForeignGeneratorTest.java new file mode 100644 index 0000000000..d9d222488d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/id/ForeignGeneratorTest.java @@ -0,0 +1,70 @@ +package org.hibernate.test.id; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.MapsId; +import javax.persistence.OneToOne; +import org.hibernate.Transaction; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +/** + * @author Nathan Xu + */ +public class ForeignGeneratorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Product.class, + ProductDetails.class + }; + } + + @Test + @TestForIssue( jiraKey = "HHH-13456") + public void testForeignGeneratorInStatelessSession() { + + inStatelessSession(statelessSession -> { + + Transaction tx = statelessSession.beginTransaction(); + + Product product = new Product(); + + ProductDetails productDetails = new ProductDetails( product ); + + statelessSession.insert( productDetails ); + + tx.commit(); + }); + } + + @Entity(name = "Product") + public static class Product { + + @Id + @GeneratedValue + private Long id; + } + + @Entity(name = "ProductDetails") + public static class ProductDetails { + + @Id + @GeneratedValue + private Long id; + + @OneToOne + @MapsId + private Product product; + + public ProductDetails() { + } + + public ProductDetails( Product product ) { + this.product = product; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/javassist/EnhancerFileNotFoundTest.java b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/enhancement/javassist/EnhancerFileNotFoundTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/javassist/EnhancerFileNotFoundTest.java rename to hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/enhancement/javassist/EnhancerFileNotFoundTest.java index b3fbb0c8dc..38647d1a6f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/javassist/EnhancerFileNotFoundTest.java +++ b/hibernate-core/src/testJavassist/java/org/hibernate/test/bytecode/enhancement/javassist/EnhancerFileNotFoundTest.java @@ -6,18 +6,20 @@ */ package org.hibernate.test.bytecode.enhancement.javassist; -import javassist.CtClass; -import org.hibernate.bytecode.enhance.internal.javassist.EnhancerImpl; -import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; -import org.hibernate.bytecode.enhance.spi.EnhancementContext; -import org.hibernate.testing.TestForIssue; -import org.junit.Test; - import java.io.FileNotFoundException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; +import javassist.CtClass; + +import org.hibernate.bytecode.enhance.internal.javassist.EnhancerImpl; +import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; +import org.hibernate.bytecode.enhance.spi.EnhancementContext; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; diff --git a/hibernate-orm-modules/module-templates/hibernate-core.xml b/hibernate-orm-modules/module-templates/hibernate-core.xml index 625dc7d61e..ec645f1fa7 100644 --- a/hibernate-orm-modules/module-templates/hibernate-core.xml +++ b/hibernate-orm-modules/module-templates/hibernate-core.xml @@ -26,7 +26,7 @@ - + diff --git a/hibernate-orm-modules/src/test/java/org/hibernate/wildfly/integrationtest/JavassistHibernateModulesOnWildflyTest.java b/hibernate-orm-modules/src/test/java/org/hibernate/wildfly/integrationtest/JavassistHibernateModulesOnWildflyTest.java new file mode 100644 index 0000000000..3a4e396182 --- /dev/null +++ b/hibernate-orm-modules/src/test/java/org/hibernate/wildfly/integrationtest/JavassistHibernateModulesOnWildflyTest.java @@ -0,0 +1,107 @@ +/* + * 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.wildfly.integrationtest; + +import java.util.Properties; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.hibernate.Session; +import org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl; +import org.hibernate.bytecode.spi.BasicProxyFactory; +import org.hibernate.bytecode.spi.BytecodeProvider; +import org.hibernate.bytecode.spi.ProxyFactoryFactory; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; +import org.hibernate.wildfly.model.Kryptonite; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.Asset; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; +import org.jboss.shrinkwrap.descriptor.api.persistence21.PersistenceDescriptor; +import org.jboss.shrinkwrap.descriptor.api.persistence21.PersistenceUnitTransactionType; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * The purpose of this test is to check that it's still possible to use Javassist as byte code provider with WildFly. + */ +@RunWith(Arquillian.class) +public class JavassistHibernateModulesOnWildflyTest { + + private static final String ORM_VERSION = Session.class.getPackage().getImplementationVersion(); + private static final String ORM_MINOR_VERSION = ORM_VERSION.substring( 0, ORM_VERSION.indexOf( ".", ORM_VERSION.indexOf( "." ) + 1) ); + + @Deployment + public static WebArchive createDeployment() { + return ShrinkWrap.create( WebArchive.class ) + .addClass( Kryptonite.class ) + .addAsWebInfResource( EmptyAsset.INSTANCE, "beans.xml" ) + .addAsResource( persistenceXml(), "META-INF/persistence.xml" ); + } + + private static Asset persistenceXml() { + PersistenceDescriptor persistenceXml = Descriptors.create( PersistenceDescriptor.class ) + .version( "2.1" ) + .createPersistenceUnit() + .name( "primary" ) + .transactionType( PersistenceUnitTransactionType._JTA ) + .jtaDataSource( "java:jboss/datasources/ExampleDS" ) + .getOrCreateProperties() + // We want to use the ORM from this build instead of the one coming with WildFly + .createProperty().name( "jboss.as.jpa.providerModule" ).value( "org.hibernate:" + ORM_MINOR_VERSION ).up() + .createProperty().name( "hibernate.hbm2ddl.auto" ).value( "create-drop" ).up() + .createProperty().name( "hibernate.allow_update_outside_transaction" ).value( "true" ).up() + .up().up(); + return new StringAsset( persistenceXml.exportAsString() ); + } + + @PersistenceContext + private EntityManager entityManager; + + @Test + public void shouldUseHibernateOrm52() { + Session session = entityManager.unwrap( Session.class ); + + Kryptonite kryptonite1 = new Kryptonite(); + kryptonite1.id = 1L; + kryptonite1.description = "Some Kryptonite"; + session.persist( kryptonite1 ); + + // EntityManager methods exposed through Session only as of 5.2 + Kryptonite loaded = session.find( Kryptonite.class, 1L ); + + assertThat( loaded.description, equalTo( "Some Kryptonite" ) ); + } + + @Test + public void shouldBeAbleToCreateProxyWithJavassist() { + Properties properties = new Properties(); + properties.setProperty( AvailableSettings.BYTECODE_PROVIDER, Environment.BYTECODE_PROVIDER_NAME_JAVASSIST ); + + // hibernate.bytecode.provider is a system property. I don't want to apply it + // to the arquillian.xml because it will change the other tests as well. + // I guess this is a more explicit way anyway to test that Javassist is available. + BytecodeProvider provider = Environment.buildBytecodeProvider( properties ); + assertThat( provider.getClass(), equalTo( BytecodeProviderImpl.class ) ); + + ProxyFactoryFactory factory = provider.getProxyFactoryFactory(); + BasicProxyFactory basicProxyFactory = factory.buildBasicProxyFactory( Kryptonite.class, null ); + Object proxy = basicProxyFactory.getProxy(); + assertThat( proxy, notNullValue() ); + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/EnhancerTestContext.java b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/EnhancerTestContext.java index bc3c80aa4d..cdcb80dd6f 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/EnhancerTestContext.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/EnhancerTestContext.java @@ -6,9 +6,6 @@ */ package org.hibernate.testing.bytecode.enhancement; -import javassist.CtClass; -import javassist.CtField; - import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; import org.hibernate.bytecode.enhance.spi.UnloadedClass; import org.hibernate.bytecode.enhance.spi.UnloadedField; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java index 6d813712de..63853abe21 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java @@ -21,6 +21,7 @@ import javax.persistence.SharedCacheMode; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.Session; +import org.hibernate.StatelessSession; import org.hibernate.Transaction; import org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl; import org.hibernate.boot.registry.BootstrapServiceRegistry; @@ -388,7 +389,7 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { sessionFactory.getCache().evictAllRegions(); } } - + protected boolean isCleanupTestDataRequired() { return false; } @@ -518,4 +519,8 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { protected void inSession(Consumer action) { TransactionUtil2.inSession( sessionFactory(), action ); } + + protected void inStatelessSession(Consumer action) { + TransactionUtil2.inStatelessSession( sessionFactory(), action ); + } }