Skip to content

Commit c5ecf5d

Browse files
committed
HHH-16742 fix implementation of TupleMetadata
fix issue when "same" selection item is assigned two different aliases
1 parent acf9495 commit c5ecf5d

File tree

2 files changed

+76
-42
lines changed

2 files changed

+76
-42
lines changed

hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.util.Calendar;
1212
import java.util.Collection;
1313
import java.util.Date;
14-
import java.util.IdentityHashMap;
1514
import java.util.List;
1615
import java.util.Map;
1716
import java.util.Optional;
@@ -128,36 +127,62 @@ protected TupleMetadata buildTupleMetadata(SqmStatement<?> statement, Class<R> r
128127

129128
private TupleMetadata getTupleMetadata(List<SqmSelection<?>> selections) {
130129
if ( getQueryOptions().getTupleTransformer() == null ) {
131-
return new TupleMetadata( buildTupleElementMap( selections ) );
130+
return new TupleMetadata( buildTupleElementArray( selections ), buildTupleAliasArray( selections ) );
132131
}
133132
else {
134133
throw new IllegalArgumentException(
135-
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer : " +
136-
getQueryOptions().getTupleTransformer()
134+
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer: "
135+
+ getQueryOptions().getTupleTransformer()
137136
);
138137
}
139138
}
140139

141-
private static Map<TupleElement<?>, Integer> buildTupleElementMap(List<SqmSelection<?>> selections) {
142-
final Map<TupleElement<?>, Integer> tupleElementMap;
143-
if ( selections.size() == 1
144-
&& selections.get( 0 ).getSelectableNode() instanceof CompoundSelection<?> ) {
145-
final List<? extends JpaSelection<?>> selectionItems =
146-
selections.get( 0 ).getSelectableNode()
147-
.getSelectionItems();
148-
tupleElementMap = new IdentityHashMap<>( selectionItems.size() );
149-
for ( int i = 0; i < selectionItems.size(); i++ ) {
150-
tupleElementMap.put( selectionItems.get( i ), i );
140+
private static TupleElement<?>[] buildTupleElementArray(List<SqmSelection<?>> selections) {
141+
final TupleElement<?>[] elements;
142+
if ( selections.size() == 1 ) {
143+
final SqmSelectableNode<?> selectableNode = selections.get(0).getSelectableNode();
144+
if ( selectableNode instanceof CompoundSelection<?> ) {
145+
final List<? extends JpaSelection<?>> selectionItems = selectableNode.getSelectionItems();
146+
elements = new TupleElement<?>[ selectionItems.size() ];
147+
for ( int i = 0; i < selectionItems.size(); i++ ) {
148+
elements[i] = selectionItems.get( i );
149+
}
150+
}
151+
else {
152+
elements = new TupleElement<?>[] { selectableNode };
153+
}
154+
}
155+
else {
156+
elements = new TupleElement<?>[ selections.size() ];
157+
for ( int i = 0; i < selections.size(); i++ ) {
158+
elements[i] = selections.get(i).getSelectableNode();
159+
}
160+
}
161+
return elements;
162+
}
163+
164+
private static String[] buildTupleAliasArray(List<SqmSelection<?>> selections) {
165+
final String[] elements;
166+
if ( selections.size() == 1 ) {
167+
final SqmSelectableNode<?> selectableNode = selections.get(0).getSelectableNode();
168+
if ( selectableNode instanceof CompoundSelection<?> ) {
169+
final List<? extends JpaSelection<?>> selectionItems = selectableNode.getSelectionItems();
170+
elements = new String[ selectionItems.size() ];
171+
for ( int i = 0; i < selectionItems.size(); i++ ) {
172+
elements[i] = selectionItems.get( i ).getAlias();
173+
}
174+
}
175+
else {
176+
elements = new String[] { selectableNode.getAlias() };
151177
}
152178
}
153179
else {
154-
tupleElementMap = new IdentityHashMap<>( selections.size() );
155-
for (int i = 0; i < selections.size(); i++ ) {
156-
final SqmSelection<?> selection = selections.get( i );
157-
tupleElementMap.put( selection.getSelectableNode(), i );
180+
elements = new String[ selections.size() ];
181+
for ( int i = 0; i < selections.size(); i++ ) {
182+
elements[i] = selections.get(i).getAlias();
158183
}
159184
}
160-
return tupleElementMap;
185+
return elements;
161186
}
162187

163188
protected void applyOptions(NamedSqmQueryMemento memento) {
@@ -173,10 +198,12 @@ protected void applyOptions(NamedSqmQueryMemento memento) {
173198

174199
if ( memento.getParameterTypes() != null ) {
175200
for ( Map.Entry<String, String> entry : memento.getParameterTypes().entrySet() ) {
176-
final QueryParameterImplementor<?> parameter = getParameterMetadata().getQueryParameter( entry.getKey() );
177-
final BasicType<?> type = getSessionFactory().getTypeConfiguration()
178-
.getBasicTypeRegistry()
179-
.getRegisteredType( entry.getValue() );
201+
final QueryParameterImplementor<?> parameter =
202+
getParameterMetadata().getQueryParameter( entry.getKey() );
203+
final BasicType<?> type =
204+
getSessionFactory().getTypeConfiguration()
205+
.getBasicTypeRegistry()
206+
.getRegisteredType( entry.getValue() );
180207
parameter.applyAnticipatedType( type );
181208
}
182209
}

hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleMetadata.java

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,58 @@
77

88
package org.hibernate.sql.results.internal;
99

10-
import java.util.Arrays;
11-
import java.util.Collections;
10+
import jakarta.persistence.TupleElement;
11+
1212
import java.util.HashMap;
13+
import java.util.IdentityHashMap;
1314
import java.util.List;
1415
import java.util.Map;
15-
import jakarta.persistence.TupleElement;
16+
17+
import static java.util.Collections.unmodifiableMap;
1618

1719
/**
1820
* Metadata about the tuple structure.
1921
*
2022
* @author Christian Beikov
23+
* @author Gavin King
2124
*/
2225
public final class TupleMetadata {
23-
private final Map<TupleElement<?>, Integer> index;
26+
private final TupleElement<?>[] elements;
27+
private final String[] aliases;
2428
private Map<String, Integer> nameIndex;
29+
private Map<TupleElement<?>, Integer> elementIndex;
2530
private List<TupleElement<?>> list;
2631

27-
public TupleMetadata(Map<TupleElement<?>, Integer> index) {
28-
this.index = index;
32+
public TupleMetadata(TupleElement<?>[] elements, String[] aliases) {
33+
this.elements = elements;
34+
this.aliases = aliases;
2935
}
3036

31-
public Integer get(TupleElement<?> tupleElement) {
32-
return index.get( tupleElement );
37+
public Integer get(TupleElement<?> element) {
38+
if ( elementIndex == null ) {
39+
final Map<TupleElement<?>, Integer> map = new IdentityHashMap<>( elements.length );
40+
for (int i = 0; i < elements.length; i++ ) {
41+
map.put( elements[i], i );
42+
}
43+
elementIndex = unmodifiableMap( map );
44+
}
45+
return elementIndex.get( element );
3346
}
3447

3548
public Integer get(String name) {
36-
Map<String, Integer> nameIndex = this.nameIndex;
3749
if ( nameIndex == null ) {
38-
nameIndex = new HashMap<>( index.size() );
39-
for ( Map.Entry<TupleElement<?>, Integer> entry : index.entrySet() ) {
40-
nameIndex.put( entry.getKey().getAlias(), entry.getValue() );
50+
final Map<String, Integer> map = new HashMap<>( aliases.length );
51+
for ( int i = 0; i < aliases.length; i++ ) {
52+
map.put( aliases[i], i );
4153
}
42-
this.nameIndex = nameIndex = Collections.unmodifiableMap( nameIndex );
54+
nameIndex = unmodifiableMap( map );
4355
}
4456
return nameIndex.get( name );
4557
}
4658

4759
public List<TupleElement<?>> getList() {
48-
List<TupleElement<?>> list = this.list;
4960
if ( list == null ) {
50-
final TupleElement<?>[] array = new TupleElement[index.size()];
51-
for ( Map.Entry<TupleElement<?>, Integer> entry : index.entrySet() ) {
52-
array[entry.getValue()] = entry.getKey();
53-
}
54-
this.list = list = Collections.unmodifiableList( Arrays.asList( array ) );
61+
list = List.of( elements );
5562
}
5663
return list;
5764
}

0 commit comments

Comments
 (0)