diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 2e69b9ae81..11d8f2031c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -12,6 +12,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -1562,117 +1563,86 @@ public class StatefulPersistenceContext implements PersistenceContext { oos.writeBoolean( defaultReadOnly ); oos.writeBoolean( hasNonReadOnlyEntities ); - if ( entitiesByKey == null ) { - oos.writeInt( 0 ); - } - else { - oos.writeInt( entitiesByKey.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + entitiesByKey.size() + "] entitiesByKey entries" ); - } - for ( Map.Entry entry : entitiesByKey.entrySet() ) { - entry.getKey().serialize( oos ); - oos.writeObject( entry.getValue() ); - } - } + final Serializer> entityKeySerializer = (entry, stream) -> { + entry.getKey().serialize( stream ); + stream.writeObject( entry.getValue() ); + }; - if ( entitiesByUniqueKey == null ) { - oos.writeInt( 0 ); - } - else { - oos.writeInt( entitiesByUniqueKey.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + entitiesByUniqueKey.size() + "] entitiesByUniqueKey entries" ); - } - for ( Map.Entry entry : entitiesByUniqueKey.entrySet() ) { - entry.getKey().serialize( oos ); - oos.writeObject( entry.getValue() ); - } - } - - if ( proxiesByKey == null ) { - oos.writeInt( 0 ); - } - else { - oos.writeInt( proxiesByKey.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + proxiesByKey.size() + "] proxiesByKey entries" ); - } - for ( Map.Entry entry : proxiesByKey.entrySet() ) { - entry.getKey().serialize( oos ); - oos.writeObject( entry.getValue() ); - } - } - - if ( entitySnapshotsByKey == null ) { - oos.writeInt( 0 ); - } - else { - oos.writeInt( entitySnapshotsByKey.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + entitySnapshotsByKey.size() + "] entitySnapshotsByKey entries" ); - } - for ( Map.Entry entry : entitySnapshotsByKey.entrySet() ) { - entry.getKey().serialize( oos ); - oos.writeObject( entry.getValue() ); - } - } + writeMapToStream( entitiesByKey, oos, "entitiesByKey", entityKeySerializer ); + writeMapToStream( + entitiesByUniqueKey, + oos, "entitiesByUniqueKey", (entry, stream) -> { + entry.getKey().serialize( stream ); + stream.writeObject( entry.getValue() ); + } + ); + writeMapToStream( proxiesByKey, oos, "proxiesByKey", entityKeySerializer ); + writeMapToStream( entitySnapshotsByKey, oos, "entitySnapshotsByKey", entityKeySerializer ); entityEntryContext.serialize( oos ); + writeMapToStream( + collectionsByKey, + oos, + "collectionsByKey", + (entry, stream) -> { + entry.getKey().serialize( stream ); + stream.writeObject( entry.getValue() ); + } + ); + writeMapToStream( + collectionEntries, + oos, + "collectionEntries", + (entry, stream) -> { + stream.writeObject( entry.getKey() ); + entry.getValue().serialize( stream ); + } + ); + writeMapToStream( + arrayHolders, + oos, + "arrayHolders", + (entry, stream) -> { + stream.writeObject( entry.getKey() ); + stream.writeObject( entry.getValue() ); + } + ); + writeCollectionToStream( nullifiableEntityKeys, oos, "nullifiableEntityKey", EntityKey::serialize ); + } - if ( collectionsByKey == null ) { + private interface Serializer { + + void serialize(E element, ObjectOutputStream oos) throws IOException; + } + + private void writeMapToStream( + Map map, + ObjectOutputStream oos, + String keysName, + Serializer> serializer) throws IOException { + if ( map == null ) { oos.writeInt( 0 ); } else { - oos.writeInt( collectionsByKey.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + collectionsByKey.size() + "] collectionsByKey entries" ); - } - for ( Map.Entry entry : collectionsByKey.entrySet() ) { - entry.getKey().serialize( oos ); - oos.writeObject( entry.getValue() ); - } + writeCollectionToStream( map.entrySet(), oos, keysName, serializer ); } + } - if ( collectionEntries == null ) { + private void writeCollectionToStream( + Collection collection, + ObjectOutputStream oos, + String keysName, + Serializer serializer) throws IOException { + if ( collection == null ) { oos.writeInt( 0 ); } else { - oos.writeInt( collectionEntries.size() ); + oos.writeInt( collection.size() ); if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + collectionEntries.size() + "] collectionEntries entries" ); + LOG.trace( "Starting serialization of [" + collection.size() + "] " + keysName + " entries" ); } - for ( Map.Entry entry : collectionEntries.entrySet() ) { - oos.writeObject( entry.getKey() ); - entry.getValue().serialize( oos ); - } - } - - if ( arrayHolders == null ) { - oos.writeInt( 0 ); - } - else { - oos.writeInt( arrayHolders.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + arrayHolders.size() + "] arrayHolders entries" ); - } - for ( Map.Entry entry : arrayHolders.entrySet() ) { - oos.writeObject( entry.getKey() ); - oos.writeObject( entry.getValue() ); - } - } - - if ( nullifiableEntityKeys == null ) { - oos.writeInt( 0 ); - } - else { - final int size = nullifiableEntityKeys.size(); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + size + "] nullifiableEntityKey entries" ); - } - oos.writeInt( size ); - for ( EntityKey entry : nullifiableEntityKeys ) { - entry.serialize( oos ); + for ( E entry : collection ) { + serializer.serialize( entry, oos ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java index 1b32e5554d..0681b676a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java @@ -55,15 +55,30 @@ public class DefaultIdentifierGeneratorFactory private static final CoreMessageLogger LOG = CoreLogging.messageLogger( DefaultIdentifierGeneratorFactory.class ); + private final boolean ignoreBeanContainer; + private ServiceRegistry serviceRegistry; private Dialect dialect; - private ConcurrentHashMap generatorStrategyToClassNameMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap generatorStrategyToClassNameMap = new ConcurrentHashMap<>(); + + private BeanContainer beanContainer; /** * Constructs a new DefaultIdentifierGeneratorFactory. */ public DefaultIdentifierGeneratorFactory() { + this( false ); + } + + /** + * Allows to explicitly control if the BeanContainer should be ignored + * (if there is one registered) when initializing any new IdentifierGenerator + * instances. + * @param ignoreBeanContainer + */ + public DefaultIdentifierGeneratorFactory(boolean ignoreBeanContainer) { + this.ignoreBeanContainer = ignoreBeanContainer; register( "uuid2", UUIDGenerator.class ); register( "guid", GUIDGenerator.class ); // can be done with UUIDGenerator + strategy register( "uuid", UUIDHexGenerator.class ); // "deprecated" for new use @@ -95,21 +110,6 @@ public class DefaultIdentifierGeneratorFactory @Override public void setDialect(Dialect dialect) { -// LOG.debugf( "Setting dialect [%s]", dialect ); -// this.dialect = dialect; -// -// if ( dialect == jdbcEnvironment.getDialect() ) { -// LOG.debugf( -// "Call to unsupported method IdentifierGeneratorFactory#setDialect; " + -// "ignoring as passed Dialect matches internal Dialect" -// ); -// } -// else { -// throw new UnsupportedOperationException( -// "Call to unsupported method IdentifierGeneratorFactory#setDialect attempting to" + -// "set a non-matching Dialect : " + dialect.getClass().getName() -// ); -// } } @SuppressWarnings("unchecked") @@ -117,9 +117,8 @@ public class DefaultIdentifierGeneratorFactory public IdentifierGenerator createIdentifierGenerator(String strategy, Type type, Properties config) { try { Class clazz = getIdentifierGeneratorClass( strategy ); - BeanContainer beanContainer = serviceRegistry.getService(ManagedBeanRegistry.class).getBeanContainer(); IdentifierGenerator identifierGenerator; - if ( generatorStrategyToClassNameMap.containsKey(strategy) || beanContainer == null ) { + if ( beanContainer == null || generatorStrategyToClassNameMap.containsKey( strategy ) ) { identifierGenerator = ( IdentifierGenerator ) clazz.newInstance(); } else { @@ -178,6 +177,10 @@ public class DefaultIdentifierGeneratorFactory this.serviceRegistry = serviceRegistry; this.dialect = serviceRegistry.getService( JdbcEnvironment.class ).getDialect(); final ConfigurationService configService = serviceRegistry.getService( ConfigurationService.class ); + if ( ! this.ignoreBeanContainer ) { + this.beanContainer = serviceRegistry.getService( ManagedBeanRegistry.class ).getBeanContainer(); + //else we just have beanContainer = null; + } final boolean useNewIdentifierGenerators = configService.getSetting( AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, @@ -185,7 +188,7 @@ public class DefaultIdentifierGeneratorFactory true ); - if(!useNewIdentifierGenerators) { + if ( ! useNewIdentifierGenerators ) { register( "sequence", SequenceGenerator.class ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/NamedQueryTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/NamedQueryTest.java index 2a1e7e52fb..3d36c4fd5d 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/NamedQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/NamedQueryTest.java @@ -19,6 +19,7 @@ import javax.persistence.Query; import org.hibernate.Session; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.query.NativeQuery; + import org.hibernate.testing.TestForIssue; import org.junit.After; import org.junit.Before; @@ -26,6 +27,7 @@ import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; /** * @author Andrea Boriero @@ -33,11 +35,11 @@ import static org.junit.Assert.assertEquals; @TestForIssue(jiraKey = "HHH-11092") public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase { - private static final String[] GAME_TITLES = {"Halo", "Grand Theft Auto", "NetHack"}; + private static final String[] GAME_TITLES = { "Halo", "Grand Theft Auto", "NetHack" }; @Override public Class[] getAnnotatedClasses() { - return new Class[] {Game.class}; + return new Class[] { Game.class }; } @Before @@ -178,6 +180,18 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase { } ); } + @Test + @TestForIssue(jiraKey = "HHH-11413") + public void testNamedNativeQueryExceptionNoRedultDefined() { + doInJPA( this::entityManagerFactory, entityManager -> { + assertThrows( + "Named query exists but its result type is not compatible", + IllegalArgumentException.class, + () -> entityManager.createNamedQuery( "NamedNativeQuery", Game.class ) + ); + } ); + } + @Entity(name = "Game") @NamedQueries(@NamedQuery(name = "NamedQuery", query = "select g from Game g where title = ?1")) @NamedNativeQueries(@NamedNativeQuery(name = "NamedNativeQuery", query = "select * from Game g where title = ?")) diff --git a/hibernate-core/src/test/java/org/hibernate/test/component/empty/EmptyInitializedNestedCompositesTest.java b/hibernate-core/src/test/java/org/hibernate/test/component/empty/EmptyInitializedNestedCompositesTest.java new file mode 100644 index 0000000000..91a0cb168c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/component/empty/EmptyInitializedNestedCompositesTest.java @@ -0,0 +1,113 @@ +/* + * 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.component.empty; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.orm.test.component.empty.ComponentEmptyEmbedded; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +/** + * Tests that an empty embeddable that is nested inside an embeddable is initialized. + * + * @author Gail Badner + */ +@TestForIssue(jiraKey = "HHH-11926") +public class EmptyInitializedNestedCompositesTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { ComponentEmptyNestedEmbeddedOwner.class }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, Boolean.valueOf( true ) ); + } + + /** + * Test empty nested composite initialization. + */ + @Test + @FailureExpected( jiraKey = "HHH-11926" ) + public void testCompositesEmpty() { + Session s = openSession(); + try { + s.getTransaction().begin(); + + ComponentEmptyNestedEmbeddedOwner owner = new ComponentEmptyNestedEmbeddedOwner(); + s.persist( owner ); + + s.flush(); + s.getTransaction().commit(); + + s.clear(); + s.getTransaction().begin(); + owner = s.get( ComponentEmptyNestedEmbeddedOwner.class, owner.getId() ); + assertNotNull( owner.getEmbedded() ); + assertNotNull( owner.getEmbedded().getNestedEmbedded() ); + + s.getTransaction().rollback(); + } + finally { + s.close(); + } + } + + @Entity(name = "EmptyNestedOwner") + public static class ComponentEmptyNestedEmbeddedOwner { + + @Id + @GeneratedValue + private Integer id; + + private EmptyNestedEmbeddedContainer embedded; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public EmptyNestedEmbeddedContainer getEmbedded() { + return embedded; + } + + public void setEmbedded(EmptyNestedEmbeddedContainer embedded) { + this.embedded = embedded; + } + + } + + @Embeddable + public static class EmptyNestedEmbeddedContainer { + public ComponentEmptyEmbedded getNestedEmbedded() { + return nestedEmbedded; + } + + public void setNestedEmbedded(ComponentEmptyEmbedded nestedEmbedded) { + this.nestedEmbedded = nestedEmbedded; + } + + private ComponentEmptyEmbedded nestedEmbedded; + } +} diff --git a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle index cd30fcacc1..ff45d6e6f2 100644 --- a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle +++ b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle @@ -29,6 +29,7 @@ dependencies { testImplementation project( ':hibernate-core' ) testImplementation libraries.junit + testImplementation libraries.validation } sourceSets.main { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeRenderingVisitor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeRenderingVisitor.java new file mode 100644 index 0000000000..8654c95d84 --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeRenderingVisitor.java @@ -0,0 +1,186 @@ +/* + * 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.jpamodelgen.util; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.NoType; +import javax.lang.model.type.NullType; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.UnionType; +import javax.lang.model.type.WildcardType; +import javax.lang.model.util.SimpleTypeVisitor8; + +/** + * @author Christian Beikov + */ +public final class TypeRenderingVisitor extends SimpleTypeVisitor8 { + + private final StringBuilder sb = new StringBuilder(); + private final Set visitedTypeVariables = new HashSet<>(); + + private TypeRenderingVisitor() { + } + + public static String toString(TypeMirror typeMirror) { + if ( typeMirror instanceof TypeVariable ) { + // Top level type variables don't need to render the upper bound as `T extends Type` + final Element typeVariableElement = ( (TypeVariable) typeMirror ).asElement(); + if ( typeVariableElement instanceof TypeParameterElement ) { + final TypeParameterElement typeParameter = (TypeParameterElement) typeVariableElement; + if ( typeParameter.getEnclosingElement().getKind() == ElementKind.METHOD ) { + // But for method level type variable we return the upper bound + // because the type variable has no meaning except for that method + typeMirror = ( (TypeVariable) typeMirror ).getUpperBound(); + } + else { + return typeParameter.toString(); + } + } + else { + typeMirror = typeVariableElement.asType(); + } + } + else if ( typeMirror instanceof IntersectionType ) { + // For top level type only the first type is relevant + typeMirror = ( (IntersectionType) typeMirror ).getBounds().get( 0 ); + } + final TypeRenderingVisitor typeRenderingVisitor = new TypeRenderingVisitor(); + typeMirror.accept( typeRenderingVisitor, null ); + return typeRenderingVisitor.sb.toString(); + } + + @Override + public Object visitPrimitive(PrimitiveType t, Object o) { + final String primitiveTypeName = getPrimitiveTypeName( t.getKind() ); + if ( primitiveTypeName != null ) { + sb.append( primitiveTypeName ); + } + return null; + } + + private static String getPrimitiveTypeName(TypeKind kind) { + switch ( kind ) { + case INT: + return "int"; + case BOOLEAN: + return "boolean"; + case BYTE: + return "byte"; + case CHAR: + return "char"; + case DOUBLE: + return "double"; + case FLOAT: + return "float"; + case LONG: + return "long"; + case SHORT: + return "short"; + case VOID: + return "void"; + } + return null; + } + + @Override + public Object visitNull(NullType t, Object o) { + return null; + } + + @Override + public Object visitArray(ArrayType t, Object o) { + t.getComponentType().accept( this, null ); + sb.append( "[]" ); + return t; + } + + @Override + public Object visitDeclared(DeclaredType t, Object o) { + sb.append( t.asElement().toString() ); + List typeArguments = t.getTypeArguments(); + if ( !typeArguments.isEmpty() ) { + sb.append( '<' ); + typeArguments.get( 0 ).accept( this, null ); + for ( int i = 1; i < typeArguments.size(); i++ ) { + sb.append( ", " ); + typeArguments.get( i ).accept( this, null ); + } + sb.append( '>' ); + } + return null; + } + + @Override + public Object visitTypeVariable(TypeVariable t, Object o) { + final Element typeVariableElement = t.asElement(); + if ( typeVariableElement instanceof TypeParameterElement ) { + final TypeParameterElement typeParameter = (TypeParameterElement) typeVariableElement; + sb.append( typeParameter ); + if ( !"java.lang.Object".equals( t.getUpperBound().toString() ) && visitedTypeVariables.add( t ) ) { + sb.append( " extends " ); + t.getUpperBound().accept( this, null ); + visitedTypeVariables.remove( t ); + } + } + else { + typeVariableElement.asType().accept( this, null ); + } + return null; + } + + @Override + public Object visitWildcard(WildcardType t, Object o) { + sb.append( '?' ); + if ( t.getExtendsBound() != null ) { + sb.append( " extends " ); + t.getExtendsBound().accept( this, null ); + } + if ( t.getSuperBound() != null ) { + sb.append( " super " ); + t.getSuperBound().accept( this, null ); + } + return null; + } + + @Override + public Object visitUnion(UnionType t, Object o) { + return null; + } + + @Override + public Object visitIntersection(IntersectionType t, Object o) { + final List bounds = t.getBounds(); + bounds.get( 0 ).accept( this, null ); + for ( int i = 0; i < bounds.size(); i++ ) { + sb.append( " & " ); + bounds.get( i ).accept( this, null ); + } + return null; + } + + @Override + public Object visitExecutable(ExecutableType t, Object o) { + return null; + } + + @Override + public Object visitNoType(NoType t, Object o) { + return null; + } +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java index 5c7610d4f3..ea5b1ac888 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java @@ -74,7 +74,7 @@ public final class TypeUtils { if ( type.getKind().isPrimitive() ) { return PRIMITIVE_WRAPPERS.get( type.getKind() ); } - return type.toString(); + return TypeRenderingVisitor.toString( type ); } public static String toArrayTypeString(ArrayType type, Context context) { diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/CollectionAsBasicTypeTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/CollectionAsBasicTypeTest.java index 10b436c510..5fd388e6ea 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/CollectionAsBasicTypeTest.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/CollectionAsBasicTypeTest.java @@ -76,14 +76,28 @@ public class CollectionAsBasicTypeTest extends CompilationTest { @TestForIssue(jiraKey = "HHH-12338") @WithClasses({PhoneBook.class}) public void testMapType() throws ClassNotFoundException, NoSuchFieldException { - assertMetamodelClassGeneratedFor(PhoneBook.class); + assertMetamodelClassGeneratedFor( PhoneBook.class ); assertAttributeTypeInMetaModelFor( PhoneBook.class, "phones", - PhoneBook.class.getDeclaredField("phones").getGenericType(), + PhoneBook.class.getDeclaredField( "phones" ).getGenericType(), "Wrong meta model type" ); } + + @Test + @TestForIssue(jiraKey = "HHH-14724") + @WithClasses({ Like.class, ConcreteLike.class }) + public void testIntersectionType() { + assertMetamodelClassGeneratedFor( ConcreteLike.class ); + } + + @Test + @TestForIssue(jiraKey = "HHH-14724") + @WithClasses({ EnumHolder.class }) + public void testRecursiveTypeVariable() { + assertMetamodelClassGeneratedFor( EnumHolder.class ); + } } diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/ConcreteLike.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/ConcreteLike.java new file mode 100644 index 0000000000..7cbb14ea8e --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/ConcreteLike.java @@ -0,0 +1,15 @@ +package org.hibernate.jpamodelgen.test.collectionbasictype; + +import javax.persistence.Entity; + +@Entity(name = "ConcreteLike") +public class ConcreteLike extends Like { + + @Override + public Reference getObject() { + return new Reference<>(); + } + + public static class Target implements Like.I1, Like.I2 { + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/EnumHolder.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/EnumHolder.java new file mode 100644 index 0000000000..7f8ea324a4 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/EnumHolder.java @@ -0,0 +1,11 @@ +package org.hibernate.jpamodelgen.test.collectionbasictype; + +import javax.persistence.Entity; + +@Entity +public class EnumHolder { + + public > E getMyEnum() { + return null; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/Goods.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/Goods.java index b494b62fe0..c1ddafefe9 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/Goods.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/Goods.java @@ -10,6 +10,7 @@ import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; +import javax.validation.constraints.NotNull; import java.util.List; /** @@ -39,6 +40,7 @@ public class Goods { this.productList = productList; } + @NotNull @Convert(converter = StringToListConverter.class) public List getTags() { return tags; diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/Like.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/Like.java new file mode 100644 index 0000000000..a7f12494a3 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/collectionbasictype/Like.java @@ -0,0 +1,29 @@ +package org.hibernate.jpamodelgen.test.collectionbasictype; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; + +/** + * @author Thomas Heigl + */ +@Entity +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +public abstract class Like { + + @Id + private Long id; + + public abstract Reference getObject(); + + interface I1 { + } + + interface I2 { + } + + public static class Reference { + } + +}