HHH-16742 fix implementation of TupleMetadata

fix issue when "same" selection item is assigned two different aliases
This commit is contained in:
Gavin 2023-05-29 18:51:09 +02:00 committed by Gavin King
parent acf9495af3
commit c5ecf5d41c
2 changed files with 76 additions and 42 deletions

View File

@ -11,7 +11,6 @@ import java.time.Instant;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -128,36 +127,62 @@ public abstract class AbstractSelectionQuery<R>
private TupleMetadata getTupleMetadata(List<SqmSelection<?>> selections) { private TupleMetadata getTupleMetadata(List<SqmSelection<?>> selections) {
if ( getQueryOptions().getTupleTransformer() == null ) { if ( getQueryOptions().getTupleTransformer() == null ) {
return new TupleMetadata( buildTupleElementMap( selections ) ); return new TupleMetadata( buildTupleElementArray( selections ), buildTupleAliasArray( selections ) );
} }
else { else {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer : " + "Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer: "
getQueryOptions().getTupleTransformer() + getQueryOptions().getTupleTransformer()
); );
} }
} }
private static Map<TupleElement<?>, Integer> buildTupleElementMap(List<SqmSelection<?>> selections) { private static TupleElement<?>[] buildTupleElementArray(List<SqmSelection<?>> selections) {
final Map<TupleElement<?>, Integer> tupleElementMap; final TupleElement<?>[] elements;
if ( selections.size() == 1 if ( selections.size() == 1 ) {
&& selections.get( 0 ).getSelectableNode() instanceof CompoundSelection<?> ) { final SqmSelectableNode<?> selectableNode = selections.get(0).getSelectableNode();
final List<? extends JpaSelection<?>> selectionItems = if ( selectableNode instanceof CompoundSelection<?> ) {
selections.get( 0 ).getSelectableNode() final List<? extends JpaSelection<?>> selectionItems = selectableNode.getSelectionItems();
.getSelectionItems(); elements = new TupleElement<?>[ selectionItems.size() ];
tupleElementMap = new IdentityHashMap<>( selectionItems.size() );
for ( int i = 0; i < selectionItems.size(); i++ ) { for ( int i = 0; i < selectionItems.size(); i++ ) {
tupleElementMap.put( selectionItems.get( i ), i ); elements[i] = selectionItems.get( i );
} }
} }
else { else {
tupleElementMap = new IdentityHashMap<>( selections.size() ); elements = new TupleElement<?>[] { selectableNode };
for (int i = 0; i < selections.size(); i++ ) {
final SqmSelection<?> selection = selections.get( i );
tupleElementMap.put( selection.getSelectableNode(), i );
} }
} }
return tupleElementMap; else {
elements = new TupleElement<?>[ selections.size() ];
for ( int i = 0; i < selections.size(); i++ ) {
elements[i] = selections.get(i).getSelectableNode();
}
}
return elements;
}
private static String[] buildTupleAliasArray(List<SqmSelection<?>> selections) {
final String[] elements;
if ( selections.size() == 1 ) {
final SqmSelectableNode<?> selectableNode = selections.get(0).getSelectableNode();
if ( selectableNode instanceof CompoundSelection<?> ) {
final List<? extends JpaSelection<?>> selectionItems = selectableNode.getSelectionItems();
elements = new String[ selectionItems.size() ];
for ( int i = 0; i < selectionItems.size(); i++ ) {
elements[i] = selectionItems.get( i ).getAlias();
}
}
else {
elements = new String[] { selectableNode.getAlias() };
}
}
else {
elements = new String[ selections.size() ];
for ( int i = 0; i < selections.size(); i++ ) {
elements[i] = selections.get(i).getAlias();
}
}
return elements;
} }
protected void applyOptions(NamedSqmQueryMemento memento) { protected void applyOptions(NamedSqmQueryMemento memento) {
@ -173,8 +198,10 @@ public abstract class AbstractSelectionQuery<R>
if ( memento.getParameterTypes() != null ) { if ( memento.getParameterTypes() != null ) {
for ( Map.Entry<String, String> entry : memento.getParameterTypes().entrySet() ) { for ( Map.Entry<String, String> entry : memento.getParameterTypes().entrySet() ) {
final QueryParameterImplementor<?> parameter = getParameterMetadata().getQueryParameter( entry.getKey() ); final QueryParameterImplementor<?> parameter =
final BasicType<?> type = getSessionFactory().getTypeConfiguration() getParameterMetadata().getQueryParameter( entry.getKey() );
final BasicType<?> type =
getSessionFactory().getTypeConfiguration()
.getBasicTypeRegistry() .getBasicTypeRegistry()
.getRegisteredType( entry.getValue() ); .getRegisteredType( entry.getValue() );
parameter.applyAnticipatedType( type ); parameter.applyAnticipatedType( type );

View File

@ -7,51 +7,58 @@
package org.hibernate.sql.results.internal; package org.hibernate.sql.results.internal;
import java.util.Arrays; import jakarta.persistence.TupleElement;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import jakarta.persistence.TupleElement;
import static java.util.Collections.unmodifiableMap;
/** /**
* Metadata about the tuple structure. * Metadata about the tuple structure.
* *
* @author Christian Beikov * @author Christian Beikov
* @author Gavin King
*/ */
public final class TupleMetadata { public final class TupleMetadata {
private final Map<TupleElement<?>, Integer> index; private final TupleElement<?>[] elements;
private final String[] aliases;
private Map<String, Integer> nameIndex; private Map<String, Integer> nameIndex;
private Map<TupleElement<?>, Integer> elementIndex;
private List<TupleElement<?>> list; private List<TupleElement<?>> list;
public TupleMetadata(Map<TupleElement<?>, Integer> index) { public TupleMetadata(TupleElement<?>[] elements, String[] aliases) {
this.index = index; this.elements = elements;
this.aliases = aliases;
} }
public Integer get(TupleElement<?> tupleElement) { public Integer get(TupleElement<?> element) {
return index.get( tupleElement ); if ( elementIndex == null ) {
final Map<TupleElement<?>, Integer> map = new IdentityHashMap<>( elements.length );
for (int i = 0; i < elements.length; i++ ) {
map.put( elements[i], i );
}
elementIndex = unmodifiableMap( map );
}
return elementIndex.get( element );
} }
public Integer get(String name) { public Integer get(String name) {
Map<String, Integer> nameIndex = this.nameIndex;
if ( nameIndex == null ) { if ( nameIndex == null ) {
nameIndex = new HashMap<>( index.size() ); final Map<String, Integer> map = new HashMap<>( aliases.length );
for ( Map.Entry<TupleElement<?>, Integer> entry : index.entrySet() ) { for ( int i = 0; i < aliases.length; i++ ) {
nameIndex.put( entry.getKey().getAlias(), entry.getValue() ); map.put( aliases[i], i );
} }
this.nameIndex = nameIndex = Collections.unmodifiableMap( nameIndex ); nameIndex = unmodifiableMap( map );
} }
return nameIndex.get( name ); return nameIndex.get( name );
} }
public List<TupleElement<?>> getList() { public List<TupleElement<?>> getList() {
List<TupleElement<?>> list = this.list;
if ( list == null ) { if ( list == null ) {
final TupleElement<?>[] array = new TupleElement[index.size()]; list = List.of( elements );
for ( Map.Entry<TupleElement<?>, Integer> entry : index.entrySet() ) {
array[entry.getValue()] = entry.getKey();
}
this.list = list = Collections.unmodifiableList( Arrays.asList( array ) );
} }
return list; return list;
} }