Skip to content

Commit b235e4c

Browse files
committed
HHH-19038 - Evaluate the session validity in AbstractPersistentCollection before actually dereferencing it
Signed-off-by: Jan Schatteman <[email protected]> HHH-19038 - add test for issue Correct minor typo in collections.adoc Signed-off-by: Jan Schatteman <[email protected]>
1 parent 8abad90 commit b235e4c

File tree

3 files changed

+170
-4
lines changed

3 files changed

+170
-4
lines changed

documentation/src/main/asciidoc/userguide/chapters/domain/collections.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ is available to have Hibernate interpret a `List` with no `@OrderColumn` and no
286286
An ID_BAG is similar to a BAG, except that it maps a generated, per-row identifier into the collection
287287
table. `@CollectionId` is the annotation to configure this identifier.
288288

289-
For details about defining an id-bad identifier, see the Javadocs for:
289+
For details about defining an id-bag identifier, see the Javadocs for:
290290

291291
* link:{javadoc-base}/org/hibernate/annotations/CollectionId.html[@CollectionId]
292292
* link:{javadoc-base}/org/hibernate/annotations/CollectionIdJavaClass.html[@CollectionIdJavaClass]

hibernate-core/src/main/java/org/hibernate/collection/spi/AbstractPersistentCollection.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ public int getSize() {
193193
return cachedSize;
194194
}
195195
else {
196+
throwLazyInitializationExceptionIfNotConnected();
196197
final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( this );
197198
if ( entry == null ) {
198-
throwLazyInitializationExceptionIfNotConnected();
199199
throwLazyInitializationException( "collection not associated with session" );
200200
throw new AssertionFailure( "impossible" );
201201
}
@@ -374,9 +374,9 @@ protected Boolean readElementExistence(final Object element) {
374374

375375
@Override
376376
public boolean elementExists(Object element) {
377+
throwLazyInitializationExceptionIfNotConnected();
377378
final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( this );
378379
if ( entry == null ) {
379-
throwLazyInitializationExceptionIfNotConnected();
380380
throwLazyInitializationException( "collection not associated with session" );
381381
throw new AssertionFailure( "impossible" );
382382
}
@@ -429,9 +429,9 @@ public Object doWork() {
429429

430430
@Override
431431
public Object elementByIndex(Object index) {
432+
throwLazyInitializationExceptionIfNotConnected();
432433
final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( this );
433434
if ( entry == null ) {
434-
throwLazyInitializationExceptionIfNotConnected();
435435
throwLazyInitializationException( "collection not associated with session" );
436436
throw new AssertionFailure( "impossible" );
437437
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.collection.basic;
6+
7+
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.Id;
10+
import jakarta.persistence.ManyToOne;
11+
import jakarta.persistence.OneToMany;
12+
import org.hibernate.Hibernate;
13+
import org.hibernate.LazyInitializationException;
14+
import org.hibernate.dialect.H2Dialect;
15+
import org.hibernate.testing.orm.junit.DomainModel;
16+
import org.hibernate.testing.orm.junit.RequiresDialect;
17+
import org.hibernate.testing.orm.junit.SessionFactory;
18+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
19+
import org.junit.jupiter.api.Test;
20+
21+
import java.util.ArrayList;
22+
import java.util.HashMap;
23+
import java.util.List;
24+
import java.util.Map;
25+
26+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
28+
import static org.junit.jupiter.api.Assertions.assertFalse;
29+
import static org.junit.jupiter.api.Assertions.assertThrows;
30+
import static org.junit.jupiter.api.Assertions.assertTrue;
31+
32+
33+
/**
34+
* @author Jan Schatteman
35+
*/
36+
@DomainModel(
37+
annotatedClasses = {DetachedElementTest.EntityWithList.class, DetachedElementTest.ListElement.class,
38+
DetachedElementTest.EntityWithMap.class, DetachedElementTest.MapElement.class}
39+
)
40+
@SessionFactory
41+
@RequiresDialect( H2Dialect.class )
42+
public class DetachedElementTest {
43+
44+
@Test
45+
public void testList(SessionFactoryScope scope) {
46+
scope.inSession(
47+
session -> {
48+
session.getTransaction().begin();
49+
EntityWithList parent = new EntityWithList(1);
50+
ListElement element = new ListElement(2, parent);
51+
session.persist( parent );
52+
session.persist( element );
53+
session.getTransaction().commit();
54+
session.clear();
55+
56+
// shouldn't throw exceptions for these operations
57+
assertDoesNotThrow(
58+
() -> {
59+
assertEquals( 1, parent.children.size() );
60+
assertFalse( parent.children.isEmpty() );
61+
assertTrue( parent.children.contains(element) );
62+
assertTrue( parent.children.remove(element) );
63+
}
64+
);
65+
66+
assertThrows( LazyInitializationException.class,
67+
() -> Hibernate.get(parent.children, 0)
68+
);
69+
}
70+
);
71+
}
72+
73+
@Test
74+
void testMap(SessionFactoryScope scope) {
75+
scope.inSession(
76+
session -> {
77+
session.getTransaction().begin();
78+
EntityWithMap parent = new EntityWithMap(1);
79+
MapElement element = new MapElement(2, parent);
80+
session.persist(parent);
81+
session.persist(element);
82+
session.getTransaction().commit();
83+
session.clear();
84+
85+
// shouldn't throw exceptions for these operations
86+
assertDoesNotThrow(
87+
() -> {
88+
assertEquals( 1, parent.children.size() );
89+
assertFalse( parent.children.isEmpty() );
90+
assertTrue( parent.children.containsKey(element.id) );
91+
assertTrue( parent.children.containsValue(element) );
92+
}
93+
);
94+
95+
assertThrows( LazyInitializationException.class,
96+
() -> Hibernate.get(parent.children, 2L)
97+
);
98+
}
99+
);
100+
}
101+
102+
@Entity
103+
public static class EntityWithList {
104+
@Id
105+
long id;
106+
107+
@OneToMany(mappedBy = "parent")
108+
List<ListElement> children = new ArrayList<>();
109+
110+
public EntityWithList(int id) {
111+
this.id = id;
112+
}
113+
114+
protected EntityWithList() {}
115+
}
116+
117+
@Entity
118+
public static class ListElement {
119+
@Id
120+
long id;
121+
122+
@ManyToOne
123+
private EntityWithList parent;
124+
125+
public ListElement(long id, EntityWithList parent) {
126+
this.id = id;
127+
this.parent = parent;
128+
parent.children.add(this);
129+
}
130+
131+
protected ListElement() {}
132+
}
133+
134+
@Entity
135+
public static class EntityWithMap {
136+
@Id
137+
long id;
138+
139+
@OneToMany(mappedBy = "parent")
140+
Map<Long, MapElement> children = new HashMap<>();
141+
142+
public EntityWithMap(int id) {
143+
this.id = id;
144+
}
145+
146+
protected EntityWithMap() {}
147+
}
148+
149+
@Entity
150+
public static class MapElement {
151+
@Id
152+
long id;
153+
154+
@ManyToOne
155+
private EntityWithMap parent;
156+
157+
public MapElement(long id, EntityWithMap parent) {
158+
this.id = id;
159+
this.parent = parent;
160+
parent.children.put(id, this);
161+
}
162+
163+
protected MapElement() {}
164+
}
165+
166+
}

0 commit comments

Comments
 (0)