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<String, EntityTypeImpl<?>> entityTypesByEntityName = 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<>();
//this list contains MappedSuperclass and EntityTypes ordered by superclass first
private List<Object> orderedMappings = new ArrayList<>();
@ -93,8 +93,8 @@ class MetadataContext {
return Collections.unmodifiableMap( entityTypes );
}
public Map<Class<?>, EmbeddableTypeImpl<?>> getEmbeddableTypeMap() {
return Collections.unmodifiableMap( embeddables );
public Set<EmbeddableTypeImpl<?>> getEmbeddableTypeMap() {
return Collections.unmodifiableSet( embeddables );
}
public Map<Class<?>, 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 );
}
}

View File

@ -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<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<?>, MappedSuperclassType<?>> jpaMappedSuperclassTypeMap = new ConcurrentHashMap<>();
private final Map<String, EntityTypeImpl<?>> 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<ManagedType<?>> getManagedTypes() {
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 );
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<EmbeddableType<?>> getEmbeddables() {
return new HashSet<>( jpaEmbeddableTypeMap.values() );
return new HashSet<>( jpaEmbeddableTypes );
}
@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,19 +6,25 @@
*/
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
ProductEntity.class,
Person.class,
Company.class
};
}
@ -32,4 +38,13 @@ public class EmbeddableMetaModelTest extends BaseEntityManagerFunctionalTestCase
} );
}
@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;
}
}