[hibernate-commits] Hibernate SVN: r18687 - in core/trunk: core/src/main/java/org/hibernate/hql/ast/tree and 3 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Feb 3 16:47:32 EST 2010


Author: steve.ebersole at jboss.com
Date: 2010-02-03 16:47:31 -0500 (Wed, 03 Feb 2010)
New Revision: 18687

Modified:
   core/trunk/core/src/main/java/org/hibernate/engine/JoinSequence.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java
   core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java
Log:
HHH-4879 - Support HQL index-refering functions for many-to-many, indexed collections

Modified: core/trunk/core/src/main/java/org/hibernate/engine/JoinSequence.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/JoinSequence.java	2010-02-03 06:58:46 UTC (rev 18686)
+++ core/trunk/core/src/main/java/org/hibernate/engine/JoinSequence.java	2010-02-03 21:47:31 UTC (rev 18687)
@@ -25,6 +25,7 @@
 package org.hibernate.engine;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -66,7 +67,7 @@
 		return buf.append( '}' ).toString();
 	}
 
-	final class Join {
+	public final class Join {
 
 		private final AssociationType associationType;
 		private final Joinable joinable;
@@ -83,23 +84,23 @@
 			this.lhsColumns = lhsColumns;
 		}
 
-		String getAlias() {
+		public String getAlias() {
 			return alias;
 		}
 
-		AssociationType getAssociationType() {
+		public AssociationType getAssociationType() {
 			return associationType;
 		}
 
-		Joinable getJoinable() {
+		public Joinable getJoinable() {
 			return joinable;
 		}
 
-		int getJoinType() {
+		public int getJoinType() {
 			return joinType;
 		}
 
-		String[] getLHSColumns() {
+		public String[] getLHSColumns() {
 			return lhsColumns;
 		}
 
@@ -283,7 +284,15 @@
 	public int getJoinCount() {
 		return joins.size();
 	}
-	
+
+	public Iterator iterateJoins() {
+		return joins.iterator();
+	}
+
+	public Join getFirstJoin() {
+		return (Join) joins.get( 0 );
+	}
+
 	public static interface Selector {
 		public boolean includeSubclasses(String alias);
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java	2010-02-03 06:58:46 UTC (rev 18686)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java	2010-02-03 21:47:31 UTC (rev 18687)
@@ -24,11 +24,15 @@
  */
 package org.hibernate.hql.ast.tree;
 
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import org.hibernate.MappingException;
 import org.hibernate.QueryException;
 import org.hibernate.param.ParameterSpecification;
+import org.hibernate.persister.collection.CollectionPropertyNames;
+import org.hibernate.type.CollectionType;
 import org.hibernate.util.ArrayHelper;
 import org.hibernate.engine.JoinSequence;
 import org.hibernate.hql.CollectionProperties;
@@ -324,10 +328,16 @@
 		PropertyMapping propertyMapping = getPropertyMapping( path );
 		// If this from element is a collection and the path is a collection property (maxIndex, etc.) then
 		// generate a sub-query.
+		//
+		// NOTE : in the case of this being a collection property in the select, not generating the subquery
+		// will not generally work.  The specific cases I am thinking about are the minIndex, maxIndex
+		// (most likely minElement, maxElement as well) cases.
+		//	todo : if ^^ is the case we should thrown an exception here rather than waiting for the sql error
+		//		if the dialect supports select-clause subqueries we could go ahead and generate the subquery also
 		if ( !inSelect && queryableCollection != null && CollectionProperties.isCollectionProperty( path ) ) {
 			Map enabledFilters = fromElement.getWalker().getEnabledFilters();
 			String subquery = CollectionSubqueryFactory.createCollectionSubquery(
-					joinSequence,
+					joinSequence.copy().setUseThetaStyle( true ),
 			        enabledFilters,
 					propertyMapping.toColumns( tableAlias, path )
 			);
@@ -397,11 +407,29 @@
 		return fromElement.getQueryable().getTableName();
 	}
 
+	private static final List SPECIAL_MANY2MANY_TREATMENT_FUNCTION_NAMES = java.util.Arrays.asList(
+			new String[] {
+					CollectionPropertyNames.COLLECTION_INDEX,
+					CollectionPropertyNames.COLLECTION_MIN_INDEX,
+					CollectionPropertyNames.COLLECTION_MAX_INDEX
+			}
+	);
+
 	PropertyMapping getPropertyMapping(String propertyName) {
 		checkInitialized();
 		if ( queryableCollection == null ) {		// Not a collection?
 			return ( PropertyMapping ) persister;	// Return the entity property mapping.
 		}
+
+		// indexed, many-to-many collections must be treated specially here if the property to
+		// be mapped touches on the index as we must adjust the alias to use the alias from
+		// the association table (which i different than the one passed in
+		if ( queryableCollection.isManyToMany()
+				&& queryableCollection.hasIndex()
+				&& SPECIAL_MANY2MANY_TREATMENT_FUNCTION_NAMES.contains( propertyName ) ) {
+			return new SpecialManyToManyCollectionPropertyMapping();
+		}
+
 		// If the property is a special collection property name, return a CollectionPropertyMapping.
 		if ( CollectionProperties.isCollectionProperty( propertyName ) ) {
 			if ( collectionPropertyMapping == null ) {
@@ -409,12 +437,14 @@
 			}
 			return collectionPropertyMapping;
 		}
+
 		if ( queryableCollection.getElementType().isAnyType() ) {
 			// collection of <many-to-any/> mappings...
 			// used to circumvent the component-collection check below...
 			return queryableCollection;
 
 		}
+
 		if ( queryableCollection.getElementType().isComponentType() ) {
 			// Collection of components.
 			if ( propertyName.equals( EntityPersister.ENTITY_ID ) ) {
@@ -425,17 +455,9 @@
 	}
 
 	public boolean isCollectionOfValuesOrComponents() {
-		if ( persister == null ) {
-			if ( queryableCollection == null ) {
-				return false;
-			}
-			else {
-				return !queryableCollection.getElementType().isEntityType();
-			}
-		}
-		else {
-			return false;
-		}
+		return persister == null
+				&& queryableCollection != null
+				&& !queryableCollection.getElementType().isEntityType();
 	}
 
 	public boolean isEntity() {
@@ -449,4 +471,62 @@
 	public void setIndexCollectionSelectorParamSpec(ParameterSpecification indexCollectionSelectorParamSpec) {
 		this.indexCollectionSelectorParamSpec = indexCollectionSelectorParamSpec;
 	}
+
+	private class SpecialManyToManyCollectionPropertyMapping implements PropertyMapping {
+		/**
+		 * {@inheritDoc}
+		 */
+		public Type getType() {
+			return queryableCollection.getCollectionType();
+		}
+
+		private void validate(String propertyName) {
+			if ( ! ( CollectionPropertyNames.COLLECTION_INDEX.equals( propertyName )
+					|| CollectionPropertyNames.COLLECTION_MAX_INDEX.equals( propertyName )
+					|| CollectionPropertyNames.COLLECTION_MIN_INDEX.equals( propertyName ) ) ) {
+				throw new IllegalArgumentException( "Expecting index-related function call" );
+			}
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public Type toType(String propertyName) throws QueryException {
+			validate( propertyName );
+			return queryableCollection.getIndexType();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public String[] toColumns(String alias, String propertyName) throws QueryException {
+			validate( propertyName );
+			final String joinTableAlias = joinSequence.getFirstJoin().getAlias();
+			if ( CollectionPropertyNames.COLLECTION_INDEX.equals( propertyName ) ) {
+				return queryableCollection.toColumns( joinTableAlias, propertyName );
+			}
+
+			final String[] cols = queryableCollection.getIndexColumnNames( joinTableAlias );
+			if ( CollectionPropertyNames.COLLECTION_MIN_INDEX.equals( propertyName ) ) {
+				if ( cols.length != 1 ) {
+					throw new QueryException( "composite collection index in minIndex()" );
+				}
+				return new String[] { "min(" + cols[0] + ')' };
+			}
+			else {
+				if ( cols.length != 1 ) {
+					throw new QueryException( "composite collection index in maxIndex()" );
+				}
+				return new String[] { "max(" + cols[0] + ')' };
+			}
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
+			validate( propertyName );
+			return queryableCollection.toColumns( propertyName );
+		}
+	}
 }

Modified: core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java	2010-02-03 06:58:46 UTC (rev 18686)
+++ core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java	2010-02-03 21:47:31 UTC (rev 18687)
@@ -1510,27 +1510,33 @@
 		return buffer.toString();
 	}
 
-	public String[] toColumns(String alias, String propertyName)
-			throws QueryException {
-
+	/**
+	 * {@inheritDoc}
+	 */
+	public String[] toColumns(String alias, String propertyName) throws QueryException {
 		if ( "index".equals( propertyName ) ) {
-			if ( isManyToMany() ) {
-				throw new QueryException( "index() function not supported for many-to-many association" );
-			}
-			return StringHelper.qualify( alias, indexColumnNames );
+			return qualify( alias, indexColumnNames, indexFormulaTemplates );
 		}
-
 		return elementPropertyMapping.toColumns( alias, propertyName );
 	}
 
-	public String[] toColumns(String propertyName)
-			throws QueryException {
+	private String[] indexFragments;
 
+	/**
+	 * {@inheritDoc}
+	 */
+	public String[] toColumns(String propertyName) throws QueryException {
 		if ( "index".equals( propertyName ) ) {
-			if ( isManyToMany() ) {
-				throw new QueryException( "index() function not supported for many-to-many association" );
+			if ( indexFragments == null ) {
+				String[] tmp = new String[indexColumnNames.length];
+				for ( int i = 0; i < indexColumnNames.length; i++ ) {
+					tmp[i] = indexColumnNames[i] == null
+							? indexFormulas[i]
+							: indexColumnNames[i];
+					indexFragments = tmp;
+				}
 			}
-			return indexColumnNames;
+			return indexFragments;
 		}
 
 		return elementPropertyMapping.toColumns( propertyName );

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java	2010-02-03 06:58:46 UTC (rev 18686)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java	2010-02-03 21:47:31 UTC (rev 18687)
@@ -27,7 +27,20 @@
 	public static Test suite() {
 		return new FunctionalTestClassTestSuite( MapIndexFormulaTest.class );
 	}
-	
+
+	public void testIndexFunctionOnManyToManyMap() {
+		Session s = openSession();
+		s.beginTransaction();
+		s.createQuery( "from Group g join g.users u where g.name = 'something' and index(u) = 'nada'" )
+				.list();
+		s.createQuery( "from Group g join g.users u where g.name = 'something' and minindex(u) = 'nada'" )
+				.list();
+		s.createQuery( "from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada'" )
+				.list();
+		s.getTransaction().commit();
+		s.close();
+	}
+
 	public void testIndexFormulaMap() {
 		Session s = openSession();
 		Transaction t = s.beginTransaction();

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java	2010-02-03 06:58:46 UTC (rev 18686)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java	2010-02-03 21:47:31 UTC (rev 18687)
@@ -112,5 +112,17 @@
 		s.close();
 	}
 
+	public void testIndexRelatedFunctions() {
+		Session session = openSession();
+		session.beginTransaction();
+		session.createQuery( "from Employee e join e.managerBySite as m where index(m) is not null" )
+				.list();
+		session.createQuery( "from Employee e join e.managerBySite as m where minIndex(m) is not null" )
+				.list();
+		session.createQuery( "from Employee e join e.managerBySite as m where maxIndex(m) is not null" )
+				.list();
+		session.getTransaction().commit();
+		session.close();
+	}
 }
 



More information about the hibernate-commits mailing list