mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-24 04:05:39 +00:00
HHH-5396 - JPQL KEY(), ENTRY() and VALUE() does not recognize alias refs
This commit is contained in:
parent
2b213dabc3
commit
44fcd0a156
@ -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" );
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 );
|
||||
|
Loading…
x
Reference in New Issue
Block a user