Skip to content

Commit cb4fd44

Browse files
committed
[#2230] ClassCastException when with @EmebedddedId and @OnetoOne relationship
1 parent bbcf784 commit cb4fd44

File tree

5 files changed

+292
-38
lines changed

5 files changed

+292
-38
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveEmbeddedIdentifierMappingImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
1414
import org.hibernate.metamodel.mapping.JdbcMapping;
1515
import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl;
16+
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
1617
import org.hibernate.reactive.sql.results.graph.embeddable.internal.ReactiveEmbeddableFetchImpl;
1718
import org.hibernate.spi.NavigablePath;
1819
import org.hibernate.sql.ast.spi.SqlSelection;
1920
import org.hibernate.sql.ast.tree.from.TableGroup;
2021
import org.hibernate.sql.results.graph.DomainResultCreationState;
2122
import org.hibernate.sql.results.graph.Fetch;
2223
import org.hibernate.sql.results.graph.FetchParent;
24+
import org.hibernate.sql.results.graph.Fetchable;
2325

2426
public class ReactiveEmbeddedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping {
2527

@@ -104,4 +106,12 @@ public String getSqlAliasStem() {
104106
public String getFetchableName() {
105107
return delegate.getFetchableName();
106108
}
109+
110+
public Fetchable getFetchable(int position) {
111+
Fetchable fetchable = delegate.getFetchable( position );
112+
if ( fetchable instanceof ToOneAttributeMapping ) {
113+
return new ReactiveToOneAttributeMapping( (ToOneAttributeMapping) fetchable );
114+
}
115+
return fetchable;
116+
}
107117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.sql.results.graph.embeddable.internal;
7+
8+
import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState;
9+
import org.hibernate.reactive.sql.results.graph.ReactiveDomainResultsAssembler;
10+
import org.hibernate.reactive.sql.results.graph.ReactiveInitializer;
11+
import org.hibernate.sql.results.graph.Initializer;
12+
import org.hibernate.sql.results.graph.InitializerData;
13+
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
14+
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableAssembler;
15+
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
16+
17+
import java.util.concurrent.CompletionStage;
18+
19+
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
20+
21+
/**
22+
* @see org.hibernate.sql.results.graph.embeddable.internal.EmbeddableAssembler
23+
*/
24+
public class ReactiveEmbeddableAssembler extends EmbeddableAssembler implements ReactiveDomainResultsAssembler {
25+
26+
public ReactiveEmbeddableAssembler(EmbeddableInitializer<?> initializer) {
27+
super( initializer );
28+
}
29+
30+
@Override
31+
public CompletionStage<Object> reactiveAssemble(ReactiveRowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
32+
final ReactiveInitializer<InitializerData> reactiveInitializer = (ReactiveInitializer<InitializerData>) getInitializer();
33+
final InitializerData data = reactiveInitializer.getData( rowProcessingState );
34+
final Initializer.State state = data.getState();
35+
if ( state == Initializer.State.KEY_RESOLVED ) {
36+
return reactiveInitializer
37+
.reactiveResolveInstance( data )
38+
.thenApply( v -> reactiveInitializer.getResolvedInstance( data ) );
39+
}
40+
return completedFuture( reactiveInitializer.getResolvedInstance( data ) );
41+
}
42+
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
package org.hibernate.reactive.sql.results.graph.embeddable.internal;
77

88
import org.hibernate.engine.FetchTiming;
9+
import org.hibernate.reactive.sql.results.graph.entity.internal.ReactiveEntityFetchSelectImpl;
910
import org.hibernate.spi.NavigablePath;
1011
import org.hibernate.sql.results.graph.AssemblerCreationState;
12+
import org.hibernate.sql.results.graph.DomainResultAssembler;
1113
import org.hibernate.sql.results.graph.DomainResultCreationState;
14+
import org.hibernate.sql.results.graph.Fetch;
1215
import org.hibernate.sql.results.graph.FetchParent;
16+
import org.hibernate.sql.results.graph.Fetchable;
17+
import org.hibernate.sql.results.graph.Initializer;
1318
import org.hibernate.sql.results.graph.InitializerParent;
1419
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
1520
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
1621
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
22+
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
1723

1824
public class ReactiveEmbeddableFetchImpl extends EmbeddableFetchImpl {
1925

@@ -37,4 +43,20 @@ public EmbeddableInitializer<?> createInitializer(
3743
AssemblerCreationState creationState) {
3844
return new ReactiveEmbeddableInitializerImpl( this, getDiscriminatorFetch(), parent, creationState, true );
3945
}
46+
47+
@Override
48+
public DomainResultAssembler<?> createAssembler(InitializerParent<?> parent, AssemblerCreationState creationState) {
49+
Initializer<?> initializer = creationState.resolveInitializer( this, parent, this );
50+
EmbeddableInitializer<?> embeddableInitializer = initializer.asEmbeddableInitializer();
51+
return new ReactiveEmbeddableAssembler( embeddableInitializer );
52+
}
53+
54+
@Override
55+
public Fetch findFetch(Fetchable fetchable) {
56+
Fetch fetch = super.findFetch( fetchable );
57+
if ( fetch instanceof EntityFetchSelectImpl entityFetchSelect ) {
58+
return new ReactiveEntityFetchSelectImpl( entityFetchSelect );
59+
}
60+
return fetch;
61+
}
4062
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55
*/
66
package org.hibernate.reactive.sql.results.graph.embeddable.internal;
77

8+
89
import java.util.concurrent.CompletionStage;
910
import java.util.function.BiFunction;
11+
import java.util.function.Supplier;
1012

1113
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
14+
import org.hibernate.metamodel.mapping.VirtualModelPart;
15+
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
16+
import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState;
17+
import org.hibernate.reactive.sql.results.graph.ReactiveDomainResultsAssembler;
1218
import org.hibernate.reactive.sql.results.graph.ReactiveInitializer;
1319
import org.hibernate.sql.results.graph.AssemblerCreationState;
20+
import org.hibernate.sql.results.graph.DomainResultAssembler;
1421
import org.hibernate.sql.results.graph.Initializer;
1522
import org.hibernate.sql.results.graph.InitializerData;
1623
import org.hibernate.sql.results.graph.InitializerParent;
@@ -19,8 +26,11 @@
1926
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableInitializerImpl;
2027
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
2128

29+
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
2230
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
2331
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
32+
import static org.hibernate.reactive.util.impl.CompletionStages.whileLoop;
33+
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
2434

2535
public class ReactiveEmbeddableInitializerImpl extends EmbeddableInitializerImpl
2636
implements ReactiveInitializer<EmbeddableInitializerImpl.EmbeddableInitializerData> {
@@ -33,6 +43,14 @@ public ReactiveEmbeddableInitializerData(
3343
super( initializer, rowProcessingState );
3444
}
3545

46+
public Object[] getRowState(){
47+
return rowState;
48+
}
49+
50+
public EmbeddableMappingType.ConcreteEmbeddableType getEmbeddableType() {
51+
return concreteEmbeddableType;
52+
}
53+
3654
@Override
3755
public void setState(State state) {
3856
super.setState( state );
@@ -64,10 +82,140 @@ protected InitializerData createInitializerData(RowProcessingState rowProcessing
6482

6583
@Override
6684
public CompletionStage<Void> reactiveResolveInstance(EmbeddableInitializerData data) {
67-
super.resolveInstance( data );
85+
if ( data.getState() != State.KEY_RESOLVED ) {
86+
return voidFuture();
87+
}
88+
89+
data.setState( State.RESOLVED );
90+
return extractRowState( (ReactiveEmbeddableInitializerData) data )
91+
.thenAccept( unused -> prepareCompositeInstance( (ReactiveEmbeddableInitializerData) data ) );
92+
}
93+
94+
private CompletionStage<Void> extractRowState(ReactiveEmbeddableInitializerData data) {
95+
final DomainResultAssembler<?>[] subAssemblers = assemblers[data.getSubclassId()];
96+
final RowProcessingState rowProcessingState = data.getRowProcessingState();
97+
final Object[] rowState = data.getRowState();
98+
final boolean[] stateAllNull = {true};
99+
final int[] index = {0};
100+
final WhileCondition whileCondition = new WhileCondition(subAssemblers, index);
101+
return whileLoop( whileCondition, () -> {
102+
final int i = index[0];
103+
final DomainResultAssembler<?> assembler = subAssemblers[i];
104+
final CompletionStage<Void> completionStage;
105+
if ( assembler instanceof ReactiveDomainResultsAssembler<?> reactiveAssembler ) {
106+
completionStage = reactiveAssembler.reactiveAssemble( (ReactiveRowProcessingState) rowProcessingState )
107+
.thenCompose( contributorValue -> setContributorValue(
108+
contributorValue,
109+
i,
110+
rowState,
111+
stateAllNull,
112+
whileCondition
113+
) );
114+
}
115+
else {
116+
completionStage = setContributorValue(
117+
assembler == null ? null : assembler.assemble( rowProcessingState ),
118+
i,
119+
rowState,
120+
stateAllNull,
121+
whileCondition
122+
);
123+
}
124+
index[0] = i + 1;
125+
return completionStage;
126+
})
127+
.whenComplete(
128+
(unused, throwable) -> {
129+
if ( stateAllNull[0] ) {
130+
data.setState( State.MISSING );
131+
}
132+
}
133+
);
134+
}
135+
136+
private static class WhileCondition implements Supplier<Boolean> {
137+
boolean forceExit;
138+
final int maxIndex;
139+
final int[] currentIndex;
140+
141+
public WhileCondition(DomainResultAssembler<?>[] subAssemblers, int[] index) {
142+
maxIndex = subAssemblers.length;
143+
currentIndex = index;
144+
}
145+
146+
@Override
147+
public Boolean get() {
148+
return currentIndex[0] < maxIndex && !forceExit;
149+
}
150+
}
151+
152+
private CompletionStage<Void> setContributorValue(
153+
Object contributorValue,
154+
int index,
155+
Object[] rowState,
156+
boolean[] stateAllNull,
157+
WhileCondition whileCondition) {
158+
if ( contributorValue == BATCH_PROPERTY ) {
159+
rowState[index] = null;
160+
}
161+
else {
162+
rowState[index] = contributorValue;
163+
}
164+
if ( contributorValue != null ) {
165+
stateAllNull[0] = false;
166+
}
167+
else if ( isPartOfKey() ) {
168+
// If this is a foreign key and there is a null part, the whole thing has to be turned into null
169+
stateAllNull[0] = true;
170+
whileCondition.forceExit = true;
171+
}
68172
return voidFuture();
69173
}
70174

175+
private CompletionStage<Void> prepareCompositeInstance(ReactiveEmbeddableInitializerData data) {
176+
// Virtual model parts use the owning entity as container which the fetch parent access provides.
177+
// For an identifier or foreign key this is called during the resolveKey phase of the fetch parent,
178+
// so we can't use the fetch parent access in that case.
179+
final ReactiveInitializer<ReactiveEmbeddableInitializerData> parent = (ReactiveInitializer<ReactiveEmbeddableInitializerData>) getParent();
180+
if ( parent != null && getInitializedPart() instanceof VirtualModelPart && !isPartOfKey() && data.getState() != State.MISSING ) {
181+
final ReactiveEmbeddableInitializerData subData = parent.getData( data.getRowProcessingState() );
182+
return parent.reactiveResolveInstance( subData )
183+
.thenAccept(
184+
unused -> {
185+
data.setInstance( parent.getResolvedInstance( subData ) );
186+
if ( data.getState() != State.INITIALIZED && data.getInstance() == null ) {
187+
createCompositeInstance( data )
188+
.thenAccept( o -> data.setInstance( o ) );
189+
}
190+
}
191+
).thenAccept( unused -> {
192+
if ( data.getInstance() == null ) {
193+
createCompositeInstance( data )
194+
.thenAccept( data::setInstance );
195+
}
196+
} );
197+
}
198+
199+
if ( data.getInstance() == null ) {
200+
return createCompositeInstance( data )
201+
.thenAccept( data::setInstance );
202+
}
203+
return voidFuture();
204+
}
205+
206+
private CompletionStage<Object> createCompositeInstance(ReactiveEmbeddableInitializerData data) {
207+
if ( data.getState() == State.MISSING ) {
208+
return completedFuture( null );
209+
}
210+
211+
final EmbeddableInstantiator instantiator = data.getConcreteEmbeddableType() == null
212+
? getInitializedPart().getEmbeddableTypeDescriptor().getRepresentationStrategy().getInstantiator()
213+
: data.getConcreteEmbeddableType().getInstantiator();
214+
final Object instance = instantiator.instantiate( data );
215+
data.setState( State.RESOLVED );
216+
return completedFuture( instance );
217+
}
218+
71219
@Override
72220
public CompletionStage<Void> reactiveInitializeInstance(EmbeddableInitializerData data) {
73221
super.initializeInstance( data );

0 commit comments

Comments
 (0)