From 3e3b439e022b2cf3caf20c8b3e9c346723c832bf Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 30 Oct 2012 12:25:55 -0500 Subject: [PATCH] HHH-1570 - criteria-api: filtering by key-many-to-one causes invalid sql --- .../hibernate/engine/internal/JoinHelper.java | 66 ++++++++++++------- .../java/org/hibernate/loader/JoinWalker.java | 28 ++++++++ .../bidir/embedded/KeyManyToOneTest.java | 15 +++++ 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinHelper.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinHelper.java index 55df82ce3b..d195654a15 100755 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinHelper.java @@ -71,31 +71,46 @@ public final class JoinHelper { * be used in the join */ public static String[] getAliasedLHSColumnNames( - AssociationType type, - String alias, - int property, + AssociationType associationType, + String columnQualifier, + int propertyIndex, int begin, OuterJoinLoadable lhsPersister, - Mapping mapping - ) { - if ( type.useLHSPrimaryKey() ) { - return StringHelper.qualify( alias, lhsPersister.getIdentifierColumnNames() ); + Mapping mapping) { + if ( associationType.useLHSPrimaryKey() ) { + return StringHelper.qualify( columnQualifier, lhsPersister.getIdentifierColumnNames() ); } else { - String propertyName = type.getLHSPropertyName(); - if (propertyName==null) { - return ArrayHelper.slice( - lhsPersister.toColumns(alias, property), - begin, - type.getColumnSpan(mapping) - ); + String propertyName = associationType.getLHSPropertyName(); + if ( propertyName == null ) { + return ArrayHelper.slice( + toColumns( lhsPersister, columnQualifier, propertyIndex ), + begin, + associationType.getColumnSpan( mapping ) + ); } else { - return ( (PropertyMapping) lhsPersister ).toColumns(alias, propertyName); //bad cast + return ( (PropertyMapping) lhsPersister ).toColumns(columnQualifier, propertyName); //bad cast } } } - + + private static String[] toColumns(OuterJoinLoadable persister, String columnQualifier, int propertyIndex) { + if ( propertyIndex >= 0 ) { + return persister.toColumns( columnQualifier, propertyIndex ); + } + else { + final String[] cols = persister.getIdentifierColumnNames(); + final String[] result = new String[cols.length]; + + for ( int j = 0; j < cols.length; j++ ) { + result[j] = StringHelper.qualify( columnQualifier, cols[j] ); + } + + return result; + } + } + /** * Get the columns of the owning entity which are to * be used in the join @@ -116,8 +131,10 @@ public final class JoinHelper { if (propertyName==null) { //slice, to get the columns for this component //property - return ArrayHelper.slice( - lhsPersister.getSubclassPropertyColumnNames(property), + return ArrayHelper.slice( + property < 0 + ? lhsPersister.getIdentifierColumnNames() + : lhsPersister.getSubclassPropertyColumnNames(property), begin, type.getColumnSpan(mapping) ); @@ -131,11 +148,10 @@ public final class JoinHelper { } public static String getLHSTableName( - AssociationType type, - int property, - OuterJoinLoadable lhsPersister - ) { - if ( type.useLHSPrimaryKey() ) { + AssociationType type, + int propertyIndex, + OuterJoinLoadable lhsPersister) { + if ( type.useLHSPrimaryKey() || propertyIndex < 0 ) { return lhsPersister.getTableName(); } else { @@ -144,7 +160,7 @@ public final class JoinHelper { //if there is no property-ref, assume the join //is to the subclass table (ie. the table of the //subclass that the association belongs to) - return lhsPersister.getSubclassPropertyTableName(property); + return lhsPersister.getSubclassPropertyTableName(propertyIndex); } else { //handle a property-ref @@ -157,7 +173,7 @@ public final class JoinHelper { //assumes that the property-ref refers to a property of the subclass //table that the association belongs to (a reasonable guess) //TODO: fix this, add: OuterJoinLoadable.getSubclassPropertyTableName(String propertyName) - propertyRefTable = lhsPersister.getSubclassPropertyTableName(property); + propertyRefTable = lhsPersister.getSubclassPropertyTableName(propertyIndex); } return propertyRefTable; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/JoinWalker.java b/hibernate-core/src/main/java/org/hibernate/loader/JoinWalker.java index de6d11ed3e..906a6dfacc 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/JoinWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/JoinWalker.java @@ -539,6 +539,34 @@ public class JoinWalker { ); } } + + // if the entity has a composite identifier, see if we need to handle + // its sub-properties separately + final Type idType = persister.getIdentifierType(); + if ( idType.isComponentType() ) { + final CompositeType cidType = (CompositeType) idType; + if ( cidType.isEmbedded() ) { + // we have an embedded composite identifier. Most likely we need to process the composite + // properties separately, although there is an edge case where the identifier is really + // a simple identifier (single value) wrapped in a JPA @IdClass or even in the case of a + // a simple identifier (single value) wrapped in a Hibernate composite type. + // + // We really do not have a built-in method to determine that. However, generally the + // persister would report that there is single, physical identifier property which is + // explicitly at odds with the notion of "embedded composite". So we use that for now + if ( persister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) { + walkComponentTree( + cidType, + -1, + 0, + persister, + alias, + path.append( "" ), + currentDepth + ); + } + } + } } /** diff --git a/hibernate-core/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java index 1e9e6d539f..350f93713c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java @@ -26,9 +26,12 @@ import java.util.List; import org.junit.Test; +import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; +import org.hibernate.criterion.Restrictions; + import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; @@ -48,6 +51,18 @@ public class KeyManyToOneTest extends BaseCoreFunctionalTestCase { cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); } + @Test + public void testCriteriaRestrictionOnKeyManyToOne() { + Session s = openSession(); + s.beginTransaction(); + s.createQuery( "from Order o where o.customer.name = 'Acme'" ).list(); + Criteria criteria = s.createCriteria( Order.class ); + criteria.createCriteria( "customer" ).add( Restrictions.eq( "name", "Acme" ) ); + criteria.list(); + s.getTransaction().commit(); + s.close(); + } + @Test public void testSaveCascadedToKeyManyToOne() { // test cascading a save to an association with a key-many-to-one which refers to a