HHH-5396 - JPQL KEY(), ENTRY() and VALUE() does not recognize alias refs

This commit is contained in:
Steve Ebersole 2012-05-30 16:21:44 -05:00
parent 2b213dabc3
commit 44fcd0a156
5 changed files with 103 additions and 24 deletions

View File

@ -1,8 +1,10 @@
/*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -34,7 +36,7 @@
import org.hibernate.type.Type;
/**
* TODO : javadoc
* Basic support for KEY, VALUE and ENTRY based "qualified identification variables".
*
* @author Steve Ebersole
*/
@ -49,10 +51,12 @@ public String[] getColumns() {
return columns;
}
@Override
public void setScalarColumnText(int i) throws SemanticException {
ColumnHelper.generateScalarColumns( this, getColumns(), i );
}
@Override
public void resolve(
boolean generateJoin,
boolean implicitJoin,
@ -64,20 +68,36 @@ public void resolve(
FromReferenceNode mapReference = getMapReference();
mapReference.resolve( true, true );
FromElement sourceFromElement = null;
if ( isAliasRef( mapReference ) ) {
QueryableCollection collectionPersister = mapReference.getFromElement().getQueryableCollection();
if ( Map.class.isAssignableFrom( collectionPersister.getCollectionType().getReturnedClass() ) ) {
sourceFromElement = mapReference.getFromElement();
}
}
else {
if ( mapReference.getDataType().isCollectionType() ) {
CollectionType collectionType = (CollectionType) mapReference.getDataType();
if ( Map.class.isAssignableFrom( collectionType.getReturnedClass() ) ) {
FromElement sourceFromElement = mapReference.getFromElement();
sourceFromElement = mapReference.getFromElement();
}
}
}
if ( sourceFromElement == null ) {
throw nonMap();
}
setFromElement( sourceFromElement );
setDataType( resolveType( sourceFromElement.getQueryableCollection() ) );
this.columns = resolveColumns( sourceFromElement.getQueryableCollection() );
initText( this.columns );
setFirstChild( null );
return;
}
}
throw nonMap();
private boolean isAliasRef(FromReferenceNode mapReference) {
return ALIAS_REF == mapReference.getType();
}
private void initText(String[] columns) {
@ -100,6 +120,7 @@ protected SemanticException nonMap() {
return new SemanticException( expressionDescription() + " expression did not reference map property" );
}
@Override
public void resolveIndex(AST parent) throws SemanticException {
throw new UnsupportedOperationException( expressionDescription() + " expression cannot be the source for an index operation" );
}

View File

@ -1,8 +1,10 @@
/*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.hql.internal.ast.tree;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -41,7 +44,7 @@
import org.hibernate.type.Type;
/**
* TODO : javadoc
* Tree node representing reference to the entry ({@link Map.Entry}) of a Map association.
*
* @author Steve Ebersole
*/
@ -61,6 +64,7 @@ public String generateAlias(String sqlExpression) {
private int scalarColumnIndex = -1;
@Override
protected String expressionDescription() {
return "entry(*)";
}
@ -70,6 +74,8 @@ public Class getAggregationResultType() {
return Map.Entry.class;
}
@Override
@SuppressWarnings("unchecked")
protected Type resolveType(QueryableCollection collectionPersister) {
Type keyType = collectionPersister.getIndexType();
Type valueType = collectionPersister.getElementType();
@ -81,6 +87,7 @@ protected Type resolveType(QueryableCollection collectionPersister) {
return null;
}
@Override
protected String[] resolveColumns(QueryableCollection collectionPersister) {
List selections = new ArrayList();
determineKeySelectExpressions( collectionPersister, selections );
@ -118,6 +125,7 @@ private void determineKeySelectExpressions(QueryableCollection collectionPersist
}
}
@SuppressWarnings({"unchecked", "ForLoopReplaceableByForEach"})
private void appendSelectExpressions(String[] columnNames, List selections, AliasGenerator aliasGenerator) {
for ( int i = 0; i < columnNames.length; i++ ) {
selections.add(
@ -129,6 +137,7 @@ private void appendSelectExpressions(String[] columnNames, List selections, Alia
}
}
@SuppressWarnings({"unchecked", "WhileLoopReplaceableByForEach"})
private void appendSelectExpressions(SelectFragment fragment, List selections, AliasGenerator aliasGenerator) {
Iterator itr = fragment.getColumns().iterator();
while ( itr.hasNext() ) {
@ -176,10 +185,12 @@ private BasicSelectExpression(String expression, String alias) {
this.alias = alias;
}
@Override
public String getExpression() {
return expression;
}
@Override
public String getAlias() {
return alias;
}
@ -189,6 +200,7 @@ public SessionFactoryImplementor sfi() {
return getSessionFactoryHelper().getFactory();
}
@Override
public void setText(String s) {
if ( isResolved() ) {
return;
@ -196,17 +208,21 @@ public void setText(String s) {
super.setText( s );
}
@Override
public void setScalarColumn(int i) throws SemanticException {
this.scalarColumnIndex = i;
}
@Override
public int getScalarColumnIndex() {
return scalarColumnIndex;
}
@Override
public void setScalarColumnText(int i) throws SemanticException {
}
@Override
public boolean isScalar() {
// Constructors are always considered scalar results.
return true;
@ -214,23 +230,27 @@ public boolean isScalar() {
private List types = new ArrayList(4); // size=4 to prevent resizing
@Override
public List getAggregatedSelectionTypeList() {
return types;
}
private static final String[] ALIASES = { null, null };
@Override
public String[] getAggregatedAliases() {
return ALIASES;
}
private MapEntryBuilder mapEntryBuilder;
@Override
public ResultTransformer getResultTransformer() {
return mapEntryBuilder;
}
private static class MapEntryBuilder extends BasicTransformerAdapter {
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
if ( tuple.length != 2 ) {
throw new HibernateException( "Expecting exactly 2 tuples to transform into Map.Entry" );
@ -248,20 +268,24 @@ private EntryAdapter(Object key, Object value) {
this.value = value;
}
@Override
public Object getValue() {
return value;
}
@Override
public Object getKey() {
return key;
}
@Override
public Object setValue(Object value) {
Object old = this.value;
this.value = value;
return old;
}
@Override
public boolean equals(Object o) {
// IMPL NOTE : nulls are considered equal for keys and values according to Map.Entry contract
if ( this == o ) {
@ -278,6 +302,7 @@ public boolean equals(Object o) {
}
@Override
public int hashCode() {
int keyHash = key == null ? 0 : key.hashCode();
int valueHash = value == null ? 0 : value.hashCode();

View File

@ -1,8 +1,10 @@
/*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,23 +22,27 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.hql.internal.ast.tree;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.Type;
/**
* TODO : javadoc
* Tree node representing reference to the key of a Map association.
*
* @author Steve Ebersole
*/
public class MapKeyNode extends AbstractMapComponentNode {
@Override
protected String expressionDescription() {
return "key(*)";
}
@Override
protected String[] resolveColumns(QueryableCollection collectionPersister) {
return collectionPersister.getIndexColumnNames();
}
@Override
protected Type resolveType(QueryableCollection collectionPersister) {
return collectionPersister.getIndexType();
}

View File

@ -1,8 +1,10 @@
/*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,23 +22,27 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.hql.internal.ast.tree;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.Type;
/**
* TODO : javadoc
* Tree node representing reference to the value of a Map association.
*
* @author Steve Ebersole
*/
public class MapValueNode extends AbstractMapComponentNode {
@Override
protected String expressionDescription() {
return "value(*)";
}
@Override
protected String[] resolveColumns(QueryableCollection collectionPersister) {
return collectionPersister.getElementColumnNames();
}
@Override
protected Type resolveType(QueryableCollection collectionPersister) {
return collectionPersister.getElementType();
}

View File

@ -276,6 +276,18 @@ public void testJPAQLQualifiedIdentificationVariables() {
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
results = s.createQuery( "select entry(f) from Human h from h.family f" ).list();
assertEquals( 1, results.size() );
result = results.get(0);
assertTrue( Map.Entry.class.isAssignableFrom( result.getClass() ) );
entry = (Map.Entry) result;
assertTrue( String.class.isAssignableFrom( entry.getKey().getClass() ) );
assertTrue( Human.class.isAssignableFrom( entry.getValue().getClass() ) );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
results = s.createQuery( "select distinct key(h.family) from Human h" ).list();
@ -285,6 +297,15 @@ public void testJPAQLQualifiedIdentificationVariables() {
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
results = s.createQuery( "select distinct key(f) from Human h join h.family f" ).list();
assertEquals( 1, results.size() );
key = results.get(0);
assertTrue( String.class.isAssignableFrom( key.getClass() ) );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
s.delete( me );