HHH-10024 - CriteriaQuery: Join on a field named 'size' backed by an enum throws IllegalArgumentException
This commit is contained in:
parent
080014558d
commit
24901af4d9
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.hql.internal.ast.tree;
|
||||
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Represents a reference to one of the "collection properties".
|
||||
*
|
||||
* @see org.hibernate.hql.internal.CollectionProperties
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface CollectionPropertyReference {
|
||||
Type getType();
|
||||
|
||||
String[] toColumns(String tableAlias);
|
||||
}
|
|
@ -500,6 +500,10 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
|
|||
return elementType.getPropertyMapping( propertyName );
|
||||
}
|
||||
|
||||
public CollectionPropertyReference getCollectionPropertyReference(String propertyName) {
|
||||
return elementType.getCollectionPropertyReference( propertyName );
|
||||
}
|
||||
|
||||
public void setFetch(boolean fetch) {
|
||||
this.fetch = fetch;
|
||||
// Fetch can't be used with scroll() or iterate().
|
||||
|
|
|
@ -378,14 +378,15 @@ class FromElementType {
|
|||
// (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
|
||||
Map enabledFilters = fromElement.getWalker().getEnabledFilters();
|
||||
String subquery = CollectionSubqueryFactory.createCollectionSubquery(
|
||||
joinSequence.copy().setUseThetaStyle( true ),
|
||||
enabledFilters,
|
||||
propertyMapping.toColumns( tableAlias, path )
|
||||
);
|
||||
LOG.debugf( "toColumns(%s,%s) : subquery = %s", tableAlias, path, subquery );
|
||||
return new String[] {"(" + subquery + ")"};
|
||||
|
||||
// we also need to account for cases where the property name is a CollectionProperty, but
|
||||
// is also a property on the element-entity. This is handled inside `#getPropertyMapping`
|
||||
// already; here just check for propertyMapping being the same as the entity persister. Yes
|
||||
// this is hacky, but really this is difficult to handle given the current codebase.
|
||||
if ( persister != propertyMapping ) {
|
||||
// we want the subquery...
|
||||
return getCollectionPropertyReference( path ).toColumns( tableAlias );
|
||||
}
|
||||
}
|
||||
|
||||
if ( forceAlias ) {
|
||||
|
@ -485,6 +486,18 @@ class FromElementType {
|
|||
// If the property is a special collection property name, return a CollectionPropertyMapping.
|
||||
if ( CollectionProperties.isCollectionProperty( propertyName ) ) {
|
||||
if ( collectionPropertyMapping == null ) {
|
||||
// lets additionally make sure that the property name is not also the name
|
||||
// of a property on the element, assuming that the element is an entity.
|
||||
// todo : also consider composites?
|
||||
if ( persister != null ) {
|
||||
try {
|
||||
if ( persister.getPropertyType( propertyName ) != null ) {
|
||||
return (PropertyMapping) persister;
|
||||
}
|
||||
}
|
||||
catch (QueryException ignore) {
|
||||
}
|
||||
}
|
||||
collectionPropertyMapping = new CollectionPropertyMapping( queryableCollection );
|
||||
}
|
||||
return collectionPropertyMapping;
|
||||
|
@ -524,6 +537,52 @@ class FromElementType {
|
|||
this.indexCollectionSelectorParamSpec = indexCollectionSelectorParamSpec;
|
||||
}
|
||||
|
||||
public CollectionPropertyReference getCollectionPropertyReference(final String propertyName) {
|
||||
if ( queryableCollection == null ) {
|
||||
throw new QueryException( "Not a collection reference" );
|
||||
}
|
||||
|
||||
final PropertyMapping collectionPropertyMapping;
|
||||
|
||||
if ( queryableCollection.isManyToMany()
|
||||
&& queryableCollection.hasIndex()
|
||||
&& SPECIAL_MANY2MANY_TREATMENT_FUNCTION_NAMES.contains( propertyName ) ) {
|
||||
collectionPropertyMapping = new SpecialManyToManyCollectionPropertyMapping();
|
||||
}
|
||||
else if ( CollectionProperties.isCollectionProperty( propertyName ) ) {
|
||||
if ( this.collectionPropertyMapping == null ) {
|
||||
this.collectionPropertyMapping = new CollectionPropertyMapping( queryableCollection );
|
||||
}
|
||||
collectionPropertyMapping = this.collectionPropertyMapping;
|
||||
}
|
||||
else {
|
||||
collectionPropertyMapping = queryableCollection;
|
||||
}
|
||||
|
||||
return new CollectionPropertyReference() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return collectionPropertyMapping.toType( propertyName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] toColumns(final String tableAlias) {
|
||||
if ( propertyName.equalsIgnoreCase( "index" ) ) {
|
||||
return collectionPropertyMapping.toColumns( tableAlias, propertyName );
|
||||
}
|
||||
|
||||
Map enabledFilters = fromElement.getWalker().getEnabledFilters();
|
||||
String subquery = CollectionSubqueryFactory.createCollectionSubquery(
|
||||
joinSequence.copy().setUseThetaStyle( true ),
|
||||
enabledFilters,
|
||||
collectionPropertyMapping.toColumns( tableAlias, propertyName )
|
||||
);
|
||||
LOG.debugf( "toColumns(%s,%s) : subquery = %s", tableAlias, propertyName, subquery );
|
||||
return new String[] {"(" + subquery + ")"};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class SpecialManyToManyCollectionPropertyMapping implements PropertyMapping {
|
||||
@Override
|
||||
public Type getType() {
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.hql.internal.ast.TypeDiscriminatorMetadata;
|
|||
import org.hibernate.hql.internal.ast.util.ASTUtil;
|
||||
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.persister.collection.CollectionPropertyMapping;
|
||||
import org.hibernate.persister.collection.CollectionPropertyNames;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -148,7 +149,12 @@ public class MethodNode extends AbstractSelectExpression implements FunctionNode
|
|||
else {
|
||||
// Not elements(x)
|
||||
fromElement = collectionNode.getFromElement();
|
||||
setDataType( fromElement.getPropertyType( propertyName, propertyName ) );
|
||||
|
||||
final CollectionPropertyReference cpr = fromElement.getCollectionPropertyReference( propertyName );
|
||||
setDataType( cpr.getType() );
|
||||
selectColumns = cpr.toColumns( fromElement.getTableAlias() );
|
||||
|
||||
// setDataType( fromElement.getPropertyType( propertyName, propertyName ) );
|
||||
selectColumns = fromElement.toColumns( fromElement.getTableAlias(), propertyName, inSelect );
|
||||
}
|
||||
if ( collectionNode instanceof DotNode ) {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.hql;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Session;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Historically HQL allowed syntax like {@code `... where someCollectionPath.size > 1`} where
|
||||
* size refers to the collection size. However that disallows references to properties named
|
||||
* size.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-10024" )
|
||||
public class SizeAttributeReferenceTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void controlGroup() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
session.createQuery( "from EntityWithAttributeNamedSize e join e.children c where size(c) > 1" ).list();
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAttributeReference() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
session.createQuery( "from EntityWithAttributeNamedSize e join e.children c where c.size = 'abc'" ).list();
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { EntityWithAttributeNamedSize.class };
|
||||
}
|
||||
|
||||
@Entity( name = "EntityWithAttributeNamedSize" )
|
||||
@Table( name = "EntityWithAttributeNamedSize" )
|
||||
public static class EntityWithAttributeNamedSize {
|
||||
@Id
|
||||
public Integer id;
|
||||
@ManyToOne
|
||||
public EntityWithAttributeNamedSize parent;
|
||||
@OneToMany( mappedBy = "parent" )
|
||||
public Set<EntityWithAttributeNamedSize> children;
|
||||
private String size;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue