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 );
|
return elementType.getPropertyMapping( propertyName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CollectionPropertyReference getCollectionPropertyReference(String propertyName) {
|
||||||
|
return elementType.getCollectionPropertyReference( propertyName );
|
||||||
|
}
|
||||||
|
|
||||||
public void setFetch(boolean fetch) {
|
public void setFetch(boolean fetch) {
|
||||||
this.fetch = fetch;
|
this.fetch = fetch;
|
||||||
// Fetch can't be used with scroll() or iterate().
|
// Fetch can't be used with scroll() or iterate().
|
||||||
|
|
|
@ -378,14 +378,15 @@ class FromElementType {
|
||||||
// (most likely minElement, maxElement as well) cases.
|
// (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
|
// 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 the dialect supports select-clause subqueries we could go ahead and generate the subquery also
|
||||||
Map enabledFilters = fromElement.getWalker().getEnabledFilters();
|
|
||||||
String subquery = CollectionSubqueryFactory.createCollectionSubquery(
|
// we also need to account for cases where the property name is a CollectionProperty, but
|
||||||
joinSequence.copy().setUseThetaStyle( true ),
|
// is also a property on the element-entity. This is handled inside `#getPropertyMapping`
|
||||||
enabledFilters,
|
// already; here just check for propertyMapping being the same as the entity persister. Yes
|
||||||
propertyMapping.toColumns( tableAlias, path )
|
// this is hacky, but really this is difficult to handle given the current codebase.
|
||||||
);
|
if ( persister != propertyMapping ) {
|
||||||
LOG.debugf( "toColumns(%s,%s) : subquery = %s", tableAlias, path, subquery );
|
// we want the subquery...
|
||||||
return new String[] {"(" + subquery + ")"};
|
return getCollectionPropertyReference( path ).toColumns( tableAlias );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( forceAlias ) {
|
if ( forceAlias ) {
|
||||||
|
@ -485,6 +486,18 @@ class FromElementType {
|
||||||
// If the property is a special collection property name, return a CollectionPropertyMapping.
|
// If the property is a special collection property name, return a CollectionPropertyMapping.
|
||||||
if ( CollectionProperties.isCollectionProperty( propertyName ) ) {
|
if ( CollectionProperties.isCollectionProperty( propertyName ) ) {
|
||||||
if ( collectionPropertyMapping == null ) {
|
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 );
|
collectionPropertyMapping = new CollectionPropertyMapping( queryableCollection );
|
||||||
}
|
}
|
||||||
return collectionPropertyMapping;
|
return collectionPropertyMapping;
|
||||||
|
@ -524,6 +537,52 @@ class FromElementType {
|
||||||
this.indexCollectionSelectorParamSpec = indexCollectionSelectorParamSpec;
|
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 {
|
private class SpecialManyToManyCollectionPropertyMapping implements PropertyMapping {
|
||||||
@Override
|
@Override
|
||||||
public Type getType() {
|
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.ASTUtil;
|
||||||
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.persister.collection.CollectionPropertyMapping;
|
||||||
import org.hibernate.persister.collection.CollectionPropertyNames;
|
import org.hibernate.persister.collection.CollectionPropertyNames;
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
import org.hibernate.persister.collection.QueryableCollection;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
@ -148,7 +149,12 @@ public class MethodNode extends AbstractSelectExpression implements FunctionNode
|
||||||
else {
|
else {
|
||||||
// Not elements(x)
|
// Not elements(x)
|
||||||
fromElement = collectionNode.getFromElement();
|
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 );
|
selectColumns = fromElement.toColumns( fromElement.getTableAlias(), propertyName, inSelect );
|
||||||
}
|
}
|
||||||
if ( collectionNode instanceof DotNode ) {
|
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