Skip to content

Commit 3d6cc0f

Browse files
marko-bekhtaDavideD
authored andcommitted
[#2166] Hibernate Reactive eager fetching the wrong record for ManyToOne
GHQUARKUS-305
1 parent 95ad44c commit 3d6cc0f

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

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

+10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ public ReactiveEmbeddableInitializerData(
3333
super( initializer, rowProcessingState );
3434
}
3535

36+
@Override
37+
public void setState(State state) {
38+
super.setState( state );
39+
if ( State.UNINITIALIZED == state ) {
40+
// reset instance to null as otherwise EmbeddableInitializerImpl#prepareCompositeInstance
41+
// will never create a new instance after the "first row with a non-null instance" gets processed
42+
setInstance( null );
43+
}
44+
}
45+
3646
public EmbeddableMappingType.ConcreteEmbeddableType getConcreteEmbeddableType() {
3747
return super.concreteEmbeddableType;
3848
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java

+10
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ public Object getEntityIdentifier() {
5656
public void setEntityIdentifier(Object entityIdentifier) {
5757
super.entityIdentifier = entityIdentifier;
5858
}
59+
60+
@Override
61+
public void setState(State state) {
62+
super.setState( state );
63+
if ( State.UNINITIALIZED == state ) {
64+
// reset instance to null as otherwise EmbeddableInitializerImpl#prepareCompositeInstance
65+
// will never create a new instance after the "first row with a non-null instance" gets processed
66+
setInstance( null );
67+
}
68+
}
5969
}
6070

6171
private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
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;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import java.util.ArrayList;
11+
import java.util.Collection;
12+
import java.util.List;
13+
14+
import org.junit.jupiter.api.BeforeEach;
15+
import org.junit.jupiter.api.Test;
16+
17+
import io.vertx.junit5.VertxTestContext;
18+
import jakarta.persistence.Column;
19+
import jakarta.persistence.Embeddable;
20+
import jakarta.persistence.EmbeddedId;
21+
import jakarta.persistence.Entity;
22+
import jakarta.persistence.FetchType;
23+
import jakarta.persistence.JoinColumn;
24+
import jakarta.persistence.ManyToOne;
25+
import jakarta.persistence.MappedSuperclass;
26+
import jakarta.persistence.OneToMany;
27+
import jakarta.persistence.Table;
28+
29+
public class EmbeddedIdWithManyEagerTest extends BaseReactiveTest {
30+
31+
Fruit cherry;
32+
Fruit apple;
33+
Fruit banana;
34+
35+
Flower sunflower;
36+
Flower chrysanthemum;
37+
Flower rose;
38+
39+
@Override
40+
protected Collection<Class<?>> annotatedEntities() {
41+
return List.of( Flower.class, Fruit.class );
42+
}
43+
44+
@BeforeEach
45+
public void populateDb(VertxTestContext context) {
46+
Seed seed1 = new Seed( 1 );
47+
rose = new Flower( seed1, "Rose" );
48+
49+
cherry = new Fruit( seed1, "Cherry" );
50+
cherry.addFriend( rose );
51+
52+
Seed seed2 = new Seed( 2 );
53+
sunflower = new Flower( seed2, "Sunflower" );
54+
55+
apple = new Fruit( seed2, "Apple" );
56+
apple.addFriend( sunflower );
57+
58+
Seed seed3 = new Seed( 3 );
59+
chrysanthemum = new Flower( seed3, "Chrysanthemum" );
60+
61+
banana = new Fruit( seed3, "Banana" );
62+
banana.addFriend( chrysanthemum );
63+
64+
test(
65+
context,
66+
getMutinySessionFactory().withTransaction( s -> s
67+
.persistAll( cherry, rose, sunflower, apple, chrysanthemum, banana )
68+
)
69+
);
70+
}
71+
72+
@Test
73+
public void testFindWithEmbeddedId(VertxTestContext context) {
74+
test(
75+
context, getMutinySessionFactory().withTransaction( s -> s
76+
.find( Flower.class, chrysanthemum.getSeed() )
77+
.invoke( flower -> assertThat( flower.getName() ).isEqualTo( chrysanthemum.getName() ) )
78+
)
79+
);
80+
}
81+
82+
@Test
83+
public void testSelectQueryWithEmbeddedId(VertxTestContext context) {
84+
test(
85+
context, getMutinySessionFactory().withTransaction( s -> s
86+
.createSelectionQuery( "from Flower", Flower.class )
87+
.getResultList()
88+
.invoke( list -> assertThat( list.stream().map( Flower::getName ) )
89+
.containsExactlyInAnyOrder(
90+
sunflower.getName(),
91+
chrysanthemum.getName(),
92+
rose.getName()
93+
)
94+
)
95+
)
96+
);
97+
}
98+
99+
@Test
100+
public void testSelectQueryWithEmbeddedIdAndEagerRelation(VertxTestContext context) {
101+
test(
102+
context, getMutinySessionFactory().withTransaction( s -> s
103+
.createSelectionQuery( "from Flower f ", Flower.class )
104+
.getResultList()
105+
.invoke( list -> assertThat( list.stream().map( Flower::getFriend ).map( Plant::getName ) )
106+
.containsExactlyInAnyOrder(
107+
cherry.getName(),
108+
apple.getName(),
109+
banana.getName()
110+
)
111+
)
112+
)
113+
);
114+
}
115+
116+
@Embeddable
117+
public static class Seed {
118+
119+
@Column(nullable = false, updatable = false)
120+
private Integer id;
121+
122+
public Seed() {
123+
}
124+
125+
public Seed(Integer id) {
126+
this.id = id;
127+
}
128+
129+
public Integer getId() {
130+
return id;
131+
}
132+
133+
public void setId(Integer id) {
134+
this.id = id;
135+
}
136+
}
137+
138+
@MappedSuperclass
139+
public static abstract class Plant {
140+
141+
@EmbeddedId
142+
private Seed seed;
143+
144+
@Column(length = 40, unique = true)
145+
private String name;
146+
147+
protected Plant() {
148+
}
149+
150+
protected Plant(Seed seed, String name) {
151+
this.seed = seed;
152+
this.name = name;
153+
}
154+
155+
public Seed getSeed() {
156+
return seed;
157+
}
158+
159+
public void setSeed(Seed seed) {
160+
this.seed = seed;
161+
}
162+
163+
public String getName() {
164+
return name;
165+
}
166+
167+
public void setName(String name) {
168+
this.name = name;
169+
}
170+
}
171+
172+
@Entity(name = "Fruit")
173+
@Table(name = "known_fruits")
174+
public static class Fruit extends Plant {
175+
176+
@OneToMany(mappedBy = "friend", fetch = FetchType.LAZY)
177+
private List<Flower> friends = new ArrayList<>();
178+
179+
public Fruit() {
180+
}
181+
182+
public Fruit(Seed seed, String name) {
183+
super( seed, name );
184+
}
185+
186+
public void addFriend(Flower flower) {
187+
this.friends.add( flower );
188+
flower.friend = this;
189+
}
190+
191+
public List<Flower> getFriends() {
192+
return friends;
193+
}
194+
195+
@Override
196+
public String toString() {
197+
return "Fruit{" + getSeed().getId() + "," + getName() + '}';
198+
}
199+
200+
}
201+
202+
@Entity(name = "Flower")
203+
@Table(name = "known_flowers")
204+
public static class Flower extends Plant {
205+
206+
@ManyToOne(fetch = FetchType.EAGER, optional = false)
207+
@JoinColumn(name = "friend", referencedColumnName = "id", nullable = false)
208+
private Fruit friend;
209+
210+
public Flower() {
211+
}
212+
213+
public Flower(Seed seed, String name) {
214+
super( seed, name );
215+
}
216+
217+
public Fruit getFriend() {
218+
return friend;
219+
}
220+
221+
@Override
222+
public String toString() {
223+
return "Flower{" + getSeed().getId() + "," + getName() + '}';
224+
}
225+
226+
}
227+
228+
}

0 commit comments

Comments
 (0)