diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java index 7d87e15fec..edfc52477e 100755 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java @@ -57,7 +57,7 @@ class MetadataContext { private Map, EntityTypeImpl> entityTypes = new HashMap<>(); private Map> entityTypesByEntityName = new HashMap<>(); private Map> entityTypesByPersistentClass = new HashMap<>(); - private Map, EmbeddableTypeImpl> embeddables = new HashMap<>(); + private Set> embeddables = new HashSet<>(); private Map> mappedSuperclassByMappedSuperclassMapping = new HashMap<>(); //this list contains MappedSuperclass and EntityTypes ordered by superclass first private List orderedMappings = new ArrayList<>(); @@ -93,8 +93,8 @@ class MetadataContext { return Collections.unmodifiableMap( entityTypes ); } - public Map, EmbeddableTypeImpl> getEmbeddableTypeMap() { - return Collections.unmodifiableMap( embeddables ); + public Set> getEmbeddableTypeMap() { + return Collections.unmodifiableSet( embeddables ); } public Map, MappedSuperclassType> getMappedSuperclassTypeMap() { @@ -123,7 +123,7 @@ class MetadataContext { } /*package*/ void registerEmbeddedableType(EmbeddableTypeImpl embeddableType) { - embeddables.put( embeddableType.getJavaType(), embeddableType ); + embeddables.add( embeddableType ); } /*package*/ void registerMappedSuperclassType( @@ -268,7 +268,7 @@ class MetadataContext { } if ( staticMetamodelScanEnabled ) { - for ( EmbeddableTypeImpl embeddable : embeddables.values() ) { + for ( EmbeddableTypeImpl embeddable : embeddables ) { populateStaticMetamodel( embeddable ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetamodelImpl.java index cafaacd56f..d508418a1e 100755 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetamodelImpl.java @@ -16,6 +16,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArraySet; import javax.persistence.EntityGraph; import javax.persistence.NamedAttributeNode; import javax.persistence.NamedEntityGraph; @@ -90,6 +91,18 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable { private final Map, EntityTypeImpl> jpaEntityTypeMap = new ConcurrentHashMap<>(); + /** + * There can be multiple instances of an Embeddable type, each one being relative to its parent entity. + */ + private final Set> jpaEmbeddableTypes = new CopyOnWriteArraySet<>(); + /** + * That's not strictly correct in the JPA standard since for a given Java type we could have + * multiple instances of an embeddable type. Some embeddable might override attributes, but we + * can only return a single EmbeddableTypeImpl for a given Java object class. + * + * A better approach would be if the parent class and attribute name would be included as well + * when trying to locate the embeddable type. + */ private final Map, EmbeddableTypeImpl> jpaEmbeddableTypeMap = new ConcurrentHashMap<>(); private final Map, MappedSuperclassType> jpaMappedSuperclassTypeMap = new ConcurrentHashMap<>(); private final Map> jpaEntityTypesByEntityName = new ConcurrentHashMap<>(); @@ -230,7 +243,10 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable { context.wrapUp(); this.jpaEntityTypeMap.putAll( context.getEntityTypeMap() ); - this.jpaEmbeddableTypeMap.putAll( context.getEmbeddableTypeMap() ); + this.jpaEmbeddableTypes.addAll( context.getEmbeddableTypeMap() ); + for ( EmbeddableTypeImpl embeddable: jpaEmbeddableTypes ) { + this.jpaEmbeddableTypeMap.put( embeddable.getJavaType(), embeddable ); + } this.jpaMappedSuperclassTypeMap.putAll( context.getMappedSuperclassTypeMap() ); this.jpaEntityTypesByEntityName.putAll( context.getEntityTypesByEntityName() ); @@ -547,12 +563,12 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable { @Override public Set> getManagedTypes() { final int setSize = CollectionHelper.determineProperSizing( - jpaEntityTypeMap.size() + jpaMappedSuperclassTypeMap.size() + jpaEmbeddableTypeMap.size() + jpaEntityTypeMap.size() + jpaMappedSuperclassTypeMap.size() + jpaEmbeddableTypes.size() ); final Set> managedTypes = new HashSet>( setSize ); managedTypes.addAll( jpaEntityTypeMap.values() ); managedTypes.addAll( jpaMappedSuperclassTypeMap.values() ); - managedTypes.addAll( jpaEmbeddableTypeMap.values() ); + managedTypes.addAll( jpaEmbeddableTypes ); return managedTypes; } @@ -563,7 +579,7 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable { @Override public Set> getEmbeddables() { - return new HashSet<>( jpaEmbeddableTypeMap.values() ); + return new HashSet<>( jpaEmbeddableTypes ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/metamodel/Address.java b/hibernate-core/src/test/java/org/hibernate/test/metamodel/Address.java new file mode 100644 index 0000000000..a67217e99a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/metamodel/Address.java @@ -0,0 +1,34 @@ +/* + * 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.metamodel; + +import java.io.Serializable; +import javax.persistence.Embeddable; + +@Embeddable +public class Address implements Serializable { + + private String city; + + private String street; + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/metamodel/Company.java b/hibernate-core/src/test/java/org/hibernate/test/metamodel/Company.java new file mode 100644 index 0000000000..4d819d892b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/metamodel/Company.java @@ -0,0 +1,51 @@ +/* + * 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.metamodel; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.Id; + + +@Entity +public class Company implements Serializable { + + @Id + private long id = System.nanoTime(); + + @Column(nullable = false) + private String name; + + @Embedded + private Address address = new Address(); + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/metamodel/EmbeddableMetaModelTest.java b/hibernate-core/src/test/java/org/hibernate/test/metamodel/EmbeddableMetaModelTest.java index f769ae513f..4f3a19d2e8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/metamodel/EmbeddableMetaModelTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/metamodel/EmbeddableMetaModelTest.java @@ -6,30 +6,45 @@ */ package org.hibernate.test.metamodel; +import javax.persistence.metamodel.Type; + import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class EmbeddableMetaModelTest extends BaseEntityManagerFunctionalTestCase { - @Override - protected Class[] getAnnotatedClasses() { - return new Class[]{ - ProductEntity.class - }; - } + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + ProductEntity.class, + Person.class, + Company.class + }; + } - @Test - @TestForIssue(jiraKey = "HHH-11111") - public void testEmbeddableCanBeResolvedWhenUsedAsInterface() { - doInJPA( this::entityManagerFactory, entityManager -> { - assertNotNull(entityManager.getMetamodel().embeddable(LocalizedValue.class)); - assertEquals( LocalizedValue.class, ProductEntity_.description.getElementType().getJavaType() ); - assertNotNull( LocalizedValue_.value ); - } ); - } + @Test + @TestForIssue(jiraKey = "HHH-11111") + public void testEmbeddableCanBeResolvedWhenUsedAsInterface() { + doInJPA( this::entityManagerFactory, entityManager -> { + assertNotNull( entityManager.getMetamodel().embeddable( LocalizedValue.class ) ); + assertEquals( LocalizedValue.class, ProductEntity_.description.getElementType().getJavaType() ); + assertNotNull( LocalizedValue_.value ); + } ); + } + + @Test + @TestForIssue(jiraKey = "HHH-12124") + public void testEmbeddableEquality() { + doInJPA( this::entityManagerFactory, entityManager -> { + assertTrue( entityManager.getMetamodel().getEmbeddables().contains( Company_.address.getType() ) ); + assertTrue( entityManager.getMetamodel().getEmbeddables().contains( Person_.address.getType() ) ); + } ); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/metamodel/Person.java b/hibernate-core/src/test/java/org/hibernate/test/metamodel/Person.java new file mode 100644 index 0000000000..47e5cc9957 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/metamodel/Person.java @@ -0,0 +1,51 @@ +/* + * 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.metamodel; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.Id; + + +@Entity +public class Person implements Serializable { + + @Id + private long id = System.nanoTime(); + + @Column(nullable = false) + private String name; + + @Embedded + private Address address = new Address(); + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +}