[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