Skip to content

Commit 3097081

Browse files
dreab8DavideD
authored andcommitted
[#2230] ClassCastException when with @EmebedddedId and @OnetoOne relationship
1 parent 2d6f6c7 commit 3097081

File tree

5 files changed

+279
-38
lines changed

5 files changed

+279
-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: 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: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
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

1112
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
13+
import org.hibernate.metamodel.mapping.VirtualModelPart;
14+
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
15+
import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState;
16+
import org.hibernate.reactive.sql.results.graph.ReactiveDomainResultsAssembler;
1217
import org.hibernate.reactive.sql.results.graph.ReactiveInitializer;
1318
import org.hibernate.sql.results.graph.AssemblerCreationState;
19+
import org.hibernate.sql.results.graph.DomainResultAssembler;
1420
import org.hibernate.sql.results.graph.Initializer;
1521
import org.hibernate.sql.results.graph.InitializerData;
1622
import org.hibernate.sql.results.graph.InitializerParent;
@@ -19,8 +25,13 @@
1925
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableInitializerImpl;
2026
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
2127

28+
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
2229
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
30+
import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture;
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.embeddable.EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER;
34+
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
2435

2536
public class ReactiveEmbeddableInitializerImpl extends EmbeddableInitializerImpl
2637
implements ReactiveInitializer<EmbeddableInitializerImpl.EmbeddableInitializerData> {
@@ -33,6 +44,10 @@ public ReactiveEmbeddableInitializerData(
3344
super( initializer, rowProcessingState );
3445
}
3546

47+
public Object[] getRowState(){
48+
return rowState;
49+
}
50+
3651
@Override
3752
public void setState(State state) {
3853
super.setState( state );
@@ -64,10 +79,128 @@ protected InitializerData createInitializerData(RowProcessingState rowProcessing
6479

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

191+
private CompletionStage<Object> createCompositeInstance(ReactiveEmbeddableInitializerData data) {
192+
if ( data.getState() == State.MISSING ) {
193+
return nullFuture();
194+
}
195+
196+
final EmbeddableInstantiator instantiator = data.getConcreteEmbeddableType() == null
197+
? getInitializedPart().getEmbeddableTypeDescriptor().getRepresentationStrategy().getInstantiator()
198+
: data.getConcreteEmbeddableType().getInstantiator();
199+
final Object instance = instantiator.instantiate( data );
200+
data.setState( State.RESOLVED );
201+
return completedFuture( instance );
202+
}
203+
71204
@Override
72205
public CompletionStage<Void> reactiveInitializeInstance(EmbeddableInitializerData data) {
73206
super.initializeInstance( data );

0 commit comments

Comments
 (0)