HHH-12124 - The JPA Metamodel does not allow to retrieve the actual EmbeddableType since all instances are registered by the associated Java type

This commit is contained in:
Vlad Mihalcea 2018-06-18 13:15:06 +03:00
parent 84439af053
commit e6ea4828db
6 changed files with 191 additions and 24 deletions

View File

@ -57,7 +57,7 @@ class MetadataContext {
private Map<Class<?>, EntityTypeImpl<?>> entityTypes = new HashMap<>(); private Map<Class<?>, EntityTypeImpl<?>> entityTypes = new HashMap<>();
private Map<String, EntityTypeImpl<?>> entityTypesByEntityName = new HashMap<>(); private Map<String, EntityTypeImpl<?>> entityTypesByEntityName = new HashMap<>();
private Map<PersistentClass, EntityTypeImpl<?>> entityTypesByPersistentClass = new HashMap<>(); private Map<PersistentClass, EntityTypeImpl<?>> entityTypesByPersistentClass = new HashMap<>();
private Map<Class<?>, EmbeddableTypeImpl<?>> embeddables = new HashMap<>(); private Set<EmbeddableTypeImpl<?>> embeddables = new HashSet<>();
private Map<MappedSuperclass, MappedSuperclassTypeImpl<?>> mappedSuperclassByMappedSuperclassMapping = new HashMap<>(); private Map<MappedSuperclass, MappedSuperclassTypeImpl<?>> mappedSuperclassByMappedSuperclassMapping = new HashMap<>();
//this list contains MappedSuperclass and EntityTypes ordered by superclass first //this list contains MappedSuperclass and EntityTypes ordered by superclass first
private List<Object> orderedMappings = new ArrayList<>(); private List<Object> orderedMappings = new ArrayList<>();
@ -93,8 +93,8 @@ class MetadataContext {
return Collections.unmodifiableMap( entityTypes ); return Collections.unmodifiableMap( entityTypes );
} }
public Map<Class<?>, EmbeddableTypeImpl<?>> getEmbeddableTypeMap() { public Set<EmbeddableTypeImpl<?>> getEmbeddableTypeMap() {
return Collections.unmodifiableMap( embeddables ); return Collections.unmodifiableSet( embeddables );
} }
public Map<Class<?>, MappedSuperclassType<?>> getMappedSuperclassTypeMap() { public Map<Class<?>, MappedSuperclassType<?>> getMappedSuperclassTypeMap() {
@ -123,7 +123,7 @@ class MetadataContext {
} }
/*package*/ void registerEmbeddedableType(EmbeddableTypeImpl<?> embeddableType) { /*package*/ void registerEmbeddedableType(EmbeddableTypeImpl<?> embeddableType) {
embeddables.put( embeddableType.getJavaType(), embeddableType ); embeddables.add( embeddableType );
} }
/*package*/ void registerMappedSuperclassType( /*package*/ void registerMappedSuperclassType(
@ -268,7 +268,7 @@ class MetadataContext {
} }
if ( staticMetamodelScanEnabled ) { if ( staticMetamodelScanEnabled ) {
for ( EmbeddableTypeImpl embeddable : embeddables.values() ) { for ( EmbeddableTypeImpl embeddable : embeddables ) {
populateStaticMetamodel( embeddable ); populateStaticMetamodel( embeddable );
} }
} }

View File

@ -16,6 +16,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.persistence.EntityGraph; import javax.persistence.EntityGraph;
import javax.persistence.NamedAttributeNode; import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph; import javax.persistence.NamedEntityGraph;
@ -90,6 +91,18 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable {
private final Map<Class<?>, EntityTypeImpl<?>> jpaEntityTypeMap = new ConcurrentHashMap<>(); private final Map<Class<?>, EntityTypeImpl<?>> jpaEntityTypeMap = new ConcurrentHashMap<>();
/**
* There can be multiple instances of an Embeddable type, each one being relative to its parent entity.
*/
private final Set<EmbeddableTypeImpl<?>> 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<Class<?>, EmbeddableTypeImpl<?>> jpaEmbeddableTypeMap = new ConcurrentHashMap<>(); private final Map<Class<?>, EmbeddableTypeImpl<?>> jpaEmbeddableTypeMap = new ConcurrentHashMap<>();
private final Map<Class<?>, MappedSuperclassType<?>> jpaMappedSuperclassTypeMap = new ConcurrentHashMap<>(); private final Map<Class<?>, MappedSuperclassType<?>> jpaMappedSuperclassTypeMap = new ConcurrentHashMap<>();
private final Map<String, EntityTypeImpl<?>> jpaEntityTypesByEntityName = new ConcurrentHashMap<>(); private final Map<String, EntityTypeImpl<?>> jpaEntityTypesByEntityName = new ConcurrentHashMap<>();
@ -230,7 +243,10 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable {
context.wrapUp(); context.wrapUp();
this.jpaEntityTypeMap.putAll( context.getEntityTypeMap() ); 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.jpaMappedSuperclassTypeMap.putAll( context.getMappedSuperclassTypeMap() );
this.jpaEntityTypesByEntityName.putAll( context.getEntityTypesByEntityName() ); this.jpaEntityTypesByEntityName.putAll( context.getEntityTypesByEntityName() );
@ -547,12 +563,12 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable {
@Override @Override
public Set<ManagedType<?>> getManagedTypes() { public Set<ManagedType<?>> getManagedTypes() {
final int setSize = CollectionHelper.determineProperSizing( final int setSize = CollectionHelper.determineProperSizing(
jpaEntityTypeMap.size() + jpaMappedSuperclassTypeMap.size() + jpaEmbeddableTypeMap.size() jpaEntityTypeMap.size() + jpaMappedSuperclassTypeMap.size() + jpaEmbeddableTypes.size()
); );
final Set<ManagedType<?>> managedTypes = new HashSet<ManagedType<?>>( setSize ); final Set<ManagedType<?>> managedTypes = new HashSet<ManagedType<?>>( setSize );
managedTypes.addAll( jpaEntityTypeMap.values() ); managedTypes.addAll( jpaEntityTypeMap.values() );
managedTypes.addAll( jpaMappedSuperclassTypeMap.values() ); managedTypes.addAll( jpaMappedSuperclassTypeMap.values() );
managedTypes.addAll( jpaEmbeddableTypeMap.values() ); managedTypes.addAll( jpaEmbeddableTypes );
return managedTypes; return managedTypes;
} }
@ -563,7 +579,7 @@ public class MetamodelImpl implements MetamodelImplementor, Serializable {
@Override @Override
public Set<EmbeddableType<?>> getEmbeddables() { public Set<EmbeddableType<?>> getEmbeddables() {
return new HashSet<>( jpaEmbeddableTypeMap.values() ); return new HashSet<>( jpaEmbeddableTypes );
} }
@Override @Override

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -6,30 +6,45 @@
*/ */
package org.hibernate.test.metamodel; package org.hibernate.test.metamodel;
import javax.persistence.metamodel.Type;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.junit.Test; import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class EmbeddableMetaModelTest extends BaseEntityManagerFunctionalTestCase { public class EmbeddableMetaModelTest extends BaseEntityManagerFunctionalTestCase {
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class[]{ return new Class[] {
ProductEntity.class ProductEntity.class,
}; Person.class,
} Company.class
};
}
@Test @Test
@TestForIssue(jiraKey = "HHH-11111") @TestForIssue(jiraKey = "HHH-11111")
public void testEmbeddableCanBeResolvedWhenUsedAsInterface() { public void testEmbeddableCanBeResolvedWhenUsedAsInterface() {
doInJPA( this::entityManagerFactory, entityManager -> { doInJPA( this::entityManagerFactory, entityManager -> {
assertNotNull(entityManager.getMetamodel().embeddable(LocalizedValue.class)); assertNotNull( entityManager.getMetamodel().embeddable( LocalizedValue.class ) );
assertEquals( LocalizedValue.class, ProductEntity_.description.getElementType().getJavaType() ); assertEquals( LocalizedValue.class, ProductEntity_.description.getElementType().getJavaType() );
assertNotNull( LocalizedValue_.value ); 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() ) );
} );
}
} }

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}