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

View File

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