Skip to content

Commit 96c61c3

Browse files
reda-alaouidreab8
authored andcommitted
HHH-18719 Previous row state reuse can provide detached entities to the consumer
1 parent f2856cf commit 96c61c3

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.jpa;
6+
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.EntityGraph;
9+
import jakarta.persistence.FetchType;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.Id;
12+
import jakarta.persistence.ManyToOne;
13+
import jakarta.persistence.OneToMany;
14+
import jakarta.persistence.OneToOne;
15+
import jakarta.persistence.Subgraph;
16+
import jakarta.persistence.Table;
17+
import jakarta.persistence.criteria.CriteriaBuilder;
18+
import jakarta.persistence.criteria.CriteriaQuery;
19+
import jakarta.persistence.criteria.Root;
20+
import org.hibernate.Hibernate;
21+
import org.hibernate.jpa.SpecHints;
22+
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
23+
import org.hibernate.testing.orm.junit.JiraKey;
24+
import org.hibernate.testing.orm.junit.Jpa;
25+
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
28+
29+
import java.util.ArrayList;
30+
import java.util.List;
31+
import java.util.concurrent.atomic.AtomicInteger;
32+
33+
import static org.assertj.core.api.Assertions.assertThat;
34+
35+
/**
36+
* @author Réda Housni Alaoui
37+
*/
38+
@Jpa(annotatedClasses = {
39+
DetachedPreviousRowStateTest.Version.class,
40+
DetachedPreviousRowStateTest.Product.class,
41+
DetachedPreviousRowStateTest.Description.class,
42+
DetachedPreviousRowStateTest.LocalizedDescription.class
43+
})
44+
class DetachedPreviousRowStateTest {
45+
46+
@BeforeEach
47+
void setupData(EntityManagerFactoryScope scope) {
48+
scope.inTransaction( em -> {
49+
Product product = new Product();
50+
em.persist( product );
51+
52+
Description description = new Description( product );
53+
em.persist( description );
54+
55+
LocalizedDescription englishDescription = new LocalizedDescription( description );
56+
em.persist( englishDescription );
57+
LocalizedDescription frenchDescription = new LocalizedDescription( description );
58+
em.persist( frenchDescription );
59+
} );
60+
}
61+
62+
@AfterEach
63+
void cleanupData(EntityManagerFactoryScope scope) {
64+
scope.inTransaction( em -> {
65+
em.createQuery( "delete from LocalizedDescription l" ).executeUpdate();
66+
em.createQuery( "delete from Description d" ).executeUpdate();
67+
em.createQuery( "delete from Product p" ).executeUpdate();
68+
} );
69+
}
70+
71+
@Test
72+
@JiraKey(value = "HHH-18719")
73+
void test(EntityManagerFactoryScope scope) {
74+
scope.inTransaction( em -> {
75+
CriteriaBuilder cb = em.getCriteriaBuilder();
76+
CriteriaQuery<LocalizedDescription> query = cb.createQuery( LocalizedDescription.class );
77+
Root<LocalizedDescription> root = query.from( LocalizedDescription.class );
78+
query.select( root );
79+
80+
EntityGraph<LocalizedDescription> localizedDescriptionGraph =
81+
em.createEntityGraph( LocalizedDescription.class );
82+
Subgraph<Object> descriptionGraph = localizedDescriptionGraph.addSubgraph( "description" );
83+
Subgraph<Object> productGraph = descriptionGraph.addSubgraph( "product" );
84+
productGraph.addSubgraph( "versions" );
85+
86+
AtomicInteger resultCount = new AtomicInteger();
87+
88+
em.createQuery( query )
89+
.setHint( SpecHints.HINT_SPEC_LOAD_GRAPH, localizedDescriptionGraph )
90+
.getResultStream()
91+
.forEach( localizedDescription -> {
92+
resultCount.incrementAndGet();
93+
94+
assertThat( em.contains( localizedDescription.description.product ) )
95+
.withFailMessage( "'localizedDescription.description.product' is detached" )
96+
.isTrue();
97+
assertThat( Hibernate.isInitialized( localizedDescription.description.product ) )
98+
.withFailMessage( "'localizedDescription.description.product' is not initialized" )
99+
.isTrue();
100+
101+
em.flush();
102+
em.clear();
103+
} );
104+
105+
assertThat( resultCount.get() ).isEqualTo( 2 );
106+
} );
107+
}
108+
109+
@Entity(name = "Version")
110+
@Table(name = "version_tbl")
111+
public static class Version {
112+
113+
@Id
114+
@GeneratedValue
115+
private long id;
116+
117+
private String description;
118+
119+
@ManyToOne(fetch = FetchType.LAZY)
120+
private Product product;
121+
}
122+
123+
@Entity(name = "Product")
124+
@Table(name = "product_tbl")
125+
public static class Product {
126+
@Id
127+
@GeneratedValue
128+
private long id;
129+
130+
private String description;
131+
132+
@OneToMany(mappedBy = "product")
133+
private final List<Version> versions = new ArrayList<>();
134+
}
135+
136+
@Entity(name = "Description")
137+
@Table(name = "description_tbl")
138+
public static class Description {
139+
@Id
140+
@GeneratedValue
141+
private long id;
142+
143+
private String description;
144+
145+
@OneToOne(fetch = FetchType.LAZY)
146+
private Product product;
147+
148+
public Description() {
149+
}
150+
151+
public Description(Product product) {
152+
this.product = product;
153+
}
154+
}
155+
156+
@Entity(name = "LocalizedDescription")
157+
@Table(name = "localized_description_tbl")
158+
public static class LocalizedDescription {
159+
160+
@Id
161+
@GeneratedValue
162+
private long id;
163+
164+
private String localizedDescription;
165+
166+
@ManyToOne(fetch = FetchType.LAZY)
167+
private Description description;
168+
169+
public LocalizedDescription() {
170+
}
171+
172+
public LocalizedDescription(Description description) {
173+
this.description = description;
174+
}
175+
}
176+
177+
}

0 commit comments

Comments
 (0)