From c8217d9f09a23c375208bbee1115bc7f48bae643 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 9 Mar 2018 21:18:50 +0100 Subject: [PATCH] HHH-12375 Fix for eager loading same named properties with conflicting types --- .../entity/AbstractPropertyMapping.java | 15 +-- .../walking/spi/MetamodelGraphWalker.java | 20 +++- .../JoinedInheritanceEagerTest.java | 105 ++++++++++++++++++ 3 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java index 22c3d0671d3a..eeec279e7298 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java @@ -152,9 +152,9 @@ protected void addPropertyPath( String[] formulaTemplates, Mapping factory) { Type existingType = typesByPropertyPath.get( path ); - if ( existingType != null ) { + if ( existingType != null || typesByPropertyPath.containsKey( path ) ) { // If types match or the new type is not an association type, there is nothing for us to do - if ( type == existingType || !( type instanceof AssociationType ) ) { + if ( type == existingType || existingType == null || !( type instanceof AssociationType ) ) { logDuplicateRegistration( path, existingType, @@ -173,12 +173,12 @@ protected void addPropertyPath( return; } - Type newType; + Type newType = null; MetadataImplementor metadata = (MetadataImplementor) factory; if ( type instanceof AnyType ) { - // TODO: not sure how to handle any types - throw new UnsupportedOperationException( "Not yet implemented!" ); + // TODO: not sure how to handle any types. For now we just return and let the first type dictate what type the property has... + return; } else if ( type instanceof CollectionType ) { Collection thisCollection = metadata.getCollectionBinding( ( (CollectionType) existingType ).getRole() ); @@ -193,7 +193,7 @@ else if ( type instanceof CollectionType ) { return; } - // When we discover incompatible types, we register "null" as property type to signal that the property is not resolvable on the parent type + // When we discover incompatible types, we use "null" as property type to signal that the property is not resolvable on the parent type newType = null; if ( LOG.isTraceEnabled() ) { LOG.tracev( @@ -220,9 +220,6 @@ else if ( type instanceof EntityType ) { newType = getCommonType( metadata, entityType1, entityType2 ); } - else { - throw new IllegalStateException( "Unexpected association type: " + type ); - } typesByPropertyPath.put( path, newType ); // Set everything to empty to signal action has to be taken! diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetamodelGraphWalker.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetamodelGraphWalker.java index 46d8a3e13193..48312a3c1859 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetamodelGraphWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetamodelGraphWalker.java @@ -13,6 +13,7 @@ import org.hibernate.loader.PropertyPath; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.Type; @@ -90,8 +91,9 @@ public MetamodelGraphWalker(AssociationVisitationStrategy strategy, SessionFacto private void visitEntityDefinition(EntityDefinition entityDefinition) { strategy.startingEntity( entityDefinition ); + AbstractEntityPersister persister = (AbstractEntityPersister) entityDefinition.getEntityPersister(); visitIdentifierDefinition( entityDefinition.getEntityKeyDefinition() ); - visitAttributes( entityDefinition ); + visitAttributes( entityDefinition, persister); strategy.finishingEntity( entityDefinition ); } @@ -122,17 +124,17 @@ private void visitIdentifierDefinition(EntityIdentifierDefinition identifierDefi strategy.finishingEntityIdentifier( identifierDefinition ); } - private void visitAttributes(AttributeSource attributeSource) { + private void visitAttributes(AttributeSource attributeSource, AbstractEntityPersister sourcePersister) { final Iterable attributeDefinitions = attributeSource.getAttributes(); if ( attributeDefinitions == null ) { return; } for ( AttributeDefinition attributeDefinition : attributeDefinitions ) { - visitAttributeDefinition( attributeDefinition ); + visitAttributeDefinition( attributeDefinition, sourcePersister); } } - private void visitAttributeDefinition(AttributeDefinition attributeDefinition) { + private void visitAttributeDefinition(AttributeDefinition attributeDefinition, AbstractEntityPersister sourcePersister) { final PropertyPath subPath = currentPropertyPath.append( attributeDefinition.getName() ); log.debug( "Visiting attribute path : " + subPath.getFullPath() ); @@ -147,6 +149,14 @@ private void visitAttributeDefinition(AttributeDefinition attributeDefinition) { // EARLY EXIT!!! return; } + + if ( sourcePersister != null ) { + String[] columns = sourcePersister.toColumns(attributeDefinition.getName()); + // Empty columns means that the attribute is not resolvable on this persister + if ( columns.length == 0 ) { + return; + } + } } @@ -196,7 +206,7 @@ private void visitAnyDefinition(AnyMappingDefinition anyDefinition) { private void visitCompositeDefinition(CompositionDefinition compositionDefinition) { strategy.startingComposite( compositionDefinition ); - visitAttributes( compositionDefinition ); + visitAttributes( compositionDefinition, null ); strategy.finishingComposite( compositionDefinition ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java b/hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java new file mode 100644 index 000000000000..59a798d2e42d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java @@ -0,0 +1,105 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.inheritance.discriminator; + +import org.hibernate.Session; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Assert; +import org.junit.Test; + +import javax.persistence.*; +import java.util.Set; + +/** + * Test cases for joined inheritance with eager fetching. + * + * @author Christian Beikov + */ +public class JoinedInheritanceEagerTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + BaseEntity.class, + EntityA.class, + EntityB.class, + EntityC.class, + EntityD.class + }; + } + + @Test + @TestForIssue( jiraKey = "HHH-12375" ) + public void joinUnrelatedCollectionOnBaseType() { + final Session s = openSession(); + s.getTransaction().begin(); + + try { + s.createQuery("from BaseEntity b join b.attributes").list(); + Assert.fail("Expected a resolution exception for property 'attributes'!"); + } catch (IllegalArgumentException ex) { + Assert.assertTrue(ex.getMessage().contains("could not resolve property: attributes ")); + } finally { + s.getTransaction().commit(); + s.close(); + } + } + + @Entity(name = "BaseEntity") + @Inheritance(strategy = InheritanceType.JOINED) + public static class BaseEntity { + @Id + private long id; + } + + @Entity(name = "EntityA") + public static class EntityA extends BaseEntity { + @OneToMany(fetch = FetchType.LAZY) + private Set attributes; + @ManyToOne(fetch = FetchType.EAGER) + private EntityC relation; + } + + @Entity(name = "EntityB") + public static class EntityB extends BaseEntity { + @OneToMany(fetch = FetchType.LAZY) + private Set attributes; + @ManyToOne(fetch = FetchType.EAGER) + private EntityD relation; + } + + @Entity(name = "EntityC") + public static class EntityC { + @Id + private long id; + } + + @Entity(name = "EntityD") + public static class EntityD { + @Id + private long id; + } + +}