Skip to content

Commit a0f0689

Browse files
dreab8DavideD
authored andcommitted
[#2230] ClassCastException when with @EmebedddedId and @OnetoOne relationship
1 parent 9640cd5 commit a0f0689

File tree

5 files changed

+283
-38
lines changed

5 files changed

+283
-38
lines changed

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

Lines changed: 11 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,13 @@ public String getSqlAliasStem() {
104106
public String getFetchableName() {
105107
return delegate.getFetchableName();
106108
}
109+
110+
@Override
111+
public Fetchable getFetchable(int position) {
112+
Fetchable fetchable = delegate.getFetchable( position );
113+
if ( fetchable instanceof ToOneAttributeMapping ) {
114+
return new ReactiveToOneAttributeMapping( (ToOneAttributeMapping) fetchable );
115+
}
116+
return fetchable;
117+
}
107118
}
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: 21 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,19 @@ 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+
return fetch instanceof EntityFetchSelectImpl
58+
? new ReactiveEntityFetchSelectImpl( (EntityFetchSelectImpl) fetch )
59+
: fetch;
60+
}
4061
}

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

Lines changed: 139 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;
1011

12+
import org.hibernate.engine.spi.SessionFactoryImplementor;
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,12 +26,19 @@
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;
31+
import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture;
2332
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
33+
import static org.hibernate.reactive.util.impl.CompletionStages.whileLoop;
34+
import static org.hibernate.sql.results.graph.embeddable.EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER;
35+
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
2436

2537
public class ReactiveEmbeddableInitializerImpl extends EmbeddableInitializerImpl
2638
implements ReactiveInitializer<EmbeddableInitializerImpl.EmbeddableInitializerData> {
2739

40+
private final SessionFactoryImplementor sessionFactory;
41+
2842
private static class ReactiveEmbeddableInitializerData extends EmbeddableInitializerData {
2943

3044
public ReactiveEmbeddableInitializerData(
@@ -33,6 +47,10 @@ public ReactiveEmbeddableInitializerData(
3347
super( initializer, rowProcessingState );
3448
}
3549

50+
public Object[] getRowState(){
51+
return rowState;
52+
}
53+
3654
@Override
3755
public void setState(State state) {
3856
super.setState( state );
@@ -55,6 +73,7 @@ public ReactiveEmbeddableInitializerImpl(
5573
AssemblerCreationState creationState,
5674
boolean isResultInitializer) {
5775
super( resultDescriptor, discriminatorFetch, parent, creationState, isResultInitializer );
76+
sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
5877
}
5978

6079
@Override
@@ -64,10 +83,129 @@ protected InitializerData createInitializerData(RowProcessingState rowProcessing
6483

6584
@Override
6685
public CompletionStage<Void> reactiveResolveInstance(EmbeddableInitializerData data) {
67-
super.resolveInstance( data );
86+
if ( data.getState() != State.KEY_RESOLVED ) {
87+
return voidFuture();
88+
}
89+
90+
data.setState( State.RESOLVED );
91+
return extractRowState( (ReactiveEmbeddableInitializerData) data )
92+
.thenAccept( unused -> prepareCompositeInstance( (ReactiveEmbeddableInitializerData) data ) );
93+
}
94+
95+
private CompletionStage<Void> extractRowState(ReactiveEmbeddableInitializerData data) {
96+
final DomainResultAssembler<?>[] subAssemblers = assemblers[data.getSubclassId()];
97+
final RowProcessingState rowProcessingState = data.getRowProcessingState();
98+
final Object[] rowState = data.getRowState();
99+
final boolean[] stateAllNull = {true};
100+
final int[] index = {0};
101+
final boolean[] forceExit = { false };
102+
return whileLoop(
103+
() -> index[0] < subAssemblers.length && !forceExit[0],
104+
() -> {
105+
final int i = index[0]++;
106+
final DomainResultAssembler<?> assembler = subAssemblers[i];
107+
if ( assembler instanceof ReactiveDomainResultsAssembler<?> ) {
108+
return ( (ReactiveDomainResultsAssembler<?>) assembler )
109+
.reactiveAssemble( (ReactiveRowProcessingState) rowProcessingState )
110+
.thenAccept( contributorValue -> setContributorValue(
111+
contributorValue,
112+
i,
113+
rowState,
114+
stateAllNull,
115+
forceExit
116+
) );
117+
}
118+
else {
119+
setContributorValue(
120+
assembler == null ? null : assembler.assemble( rowProcessingState ),
121+
i,
122+
rowState,
123+
stateAllNull,
124+
forceExit
125+
);
126+
return voidFuture();
127+
}
128+
})
129+
.whenComplete(
130+
(unused, throwable) -> {
131+
if ( stateAllNull[0] ) {
132+
data.setState( State.MISSING );
133+
}
134+
}
135+
);
136+
}
137+
138+
private void setContributorValue(
139+
Object contributorValue,
140+
int index,
141+
Object[] rowState,
142+
boolean[] stateAllNull,
143+
boolean[] forceExit) {
144+
if ( contributorValue == BATCH_PROPERTY ) {
145+
rowState[index] = null;
146+
}
147+
else {
148+
rowState[index] = contributorValue;
149+
}
150+
if ( contributorValue != null ) {
151+
stateAllNull[0] = false;
152+
}
153+
else if ( isPartOfKey() ) {
154+
// If this is a foreign key and there is a null part, the whole thing has to be turned into null
155+
stateAllNull[0] = true;
156+
forceExit[0] = true;
157+
}
158+
}
159+
160+
private CompletionStage<Void> prepareCompositeInstance(ReactiveEmbeddableInitializerData data) {
161+
// Virtual model parts use the owning entity as container which the fetch parent access provides.
162+
// For an identifier or foreign key this is called during the resolveKey phase of the fetch parent,
163+
// so we can't use the fetch parent access in that case.
164+
final ReactiveInitializer<ReactiveEmbeddableInitializerData> parent = (ReactiveInitializer<ReactiveEmbeddableInitializerData>) getParent();
165+
if ( parent != null && getInitializedPart() instanceof VirtualModelPart && !isPartOfKey() && data.getState() != State.MISSING ) {
166+
final ReactiveEmbeddableInitializerData subData = parent.getData( data.getRowProcessingState() );
167+
return parent
168+
.reactiveResolveInstance( subData )
169+
.thenCompose(
170+
unused -> {
171+
data.setInstance( parent.getResolvedInstance( subData ) );
172+
if ( data.getState() == State.INITIALIZED ) {
173+
return voidFuture();
174+
}
175+
return doCreateCompositeInstance( data )
176+
.thenAccept( v -> EMBEDDED_LOAD_LOGGER.debugf(
177+
"Created composite instance [%s]",
178+
getNavigablePath()
179+
) );
180+
} );
181+
}
182+
183+
return doCreateCompositeInstance( data )
184+
.thenAccept( v -> EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s]", getNavigablePath() ) );
185+
186+
}
187+
188+
private CompletionStage<Void> doCreateCompositeInstance(ReactiveEmbeddableInitializerData data) {
189+
if ( data.getInstance() == null ) {
190+
return createCompositeInstance( data )
191+
.thenAccept( data::setInstance );
192+
}
68193
return voidFuture();
69194
}
70195

196+
private CompletionStage<Object> createCompositeInstance(ReactiveEmbeddableInitializerData data) {
197+
if ( data.getState() == State.MISSING ) {
198+
return nullFuture();
199+
}
200+
201+
final EmbeddableInstantiator instantiator = data.getConcreteEmbeddableType() == null
202+
? getInitializedPart().getEmbeddableTypeDescriptor().getRepresentationStrategy().getInstantiator()
203+
: data.getConcreteEmbeddableType().getInstantiator();
204+
final Object instance = instantiator.instantiate( data, sessionFactory );
205+
data.setState( State.RESOLVED );
206+
return completedFuture( instance );
207+
}
208+
71209
@Override
72210
public CompletionStage<Void> reactiveInitializeInstance(EmbeddableInitializerData data) {
73211
super.initializeInstance( data );

0 commit comments

Comments
 (0)