Skip to content

Commit f3e62ea

Browse files
yrodieregsmet
authored andcommitted
HHH-7686 Allow lazy loading outside of a transaction after dynamic map proxy deserialization if the proper settings were enabled
In theory, trying to deserialize MapLazyInitializer instances that were serialized before this patch should still work, although using such instances (i.e. trying to access any method on the proxy) would still fail, just like it used to before this patch.
1 parent 1522efc commit f3e62ea

File tree

6 files changed

+95
-12
lines changed

6 files changed

+95
-12
lines changed

hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,17 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
4545
private boolean allowLoadOutsideTransaction;
4646

4747
/**
48-
* For serialization from the non-pojo initializers (HHH-3309)
48+
* @deprecated This constructor was initially intended for serialization only, and is not useful anymore.
49+
* In any case it should not be relied on by user code.
50+
* Subclasses should rather implement Serializable with an {@code Object writeReplace()} method returning
51+
* a subclass of {@link AbstractSerializableProxy},
52+
* which in turn implements Serializable and an {@code Object readResolve()} method
53+
* instantiating the {@link AbstractLazyInitializer} subclass
54+
* and calling {@link AbstractSerializableProxy#afterDeserialization(AbstractLazyInitializer)} on it.
55+
* See {@link org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor} and
56+
* {@link org.hibernate.proxy.pojo.bytebuddy.SerializableProxy} for examples.
4957
*/
58+
@Deprecated
5059
protected AbstractLazyInitializer() {
5160
}
5261

@@ -240,7 +249,7 @@ else if ( session.isOpen() && session.isConnected() ) {
240249
* and the entity being proxied has been loaded and added to the persistence context
241250
* of that session since the proxy was created.
242251
*/
243-
protected final void initializeWithoutLoadIfPossible() {
252+
public final void initializeWithoutLoadIfPossible() {
244253
if ( !initialized && session != null && session.isOpen() ) {
245254
final EntityKey key = session.generateEntityKey(
246255
getIdentifier(),
@@ -380,7 +389,7 @@ public final void setReadOnly(boolean readOnly) {
380389
*
381390
* @throws IllegalStateException if isReadOnlySettingAvailable() == true
382391
*/
383-
protected final Boolean isReadOnlyBeforeAttachedToSession() {
392+
public final Boolean isReadOnlyBeforeAttachedToSession() {
384393
if ( isReadOnlySettingAvailable() ) {
385394
throw new IllegalStateException(
386395
"Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"

hibernate-core/src/main/java/org/hibernate/proxy/AbstractSerializableProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import java.io.Serializable;
1010

1111
/**
12-
* Convenience base class for SerializableProxy.
12+
* Convenience base class for the serialized form of {@link AbstractLazyInitializer}.
1313
*
1414
* @author Gail Badner
1515
*/

hibernate-core/src/main/java/org/hibernate/proxy/map/MapLazyInitializer.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,20 @@ public Class getPersistentClass() {
3131
throw new UnsupportedOperationException("dynamic-map entity representation");
3232
}
3333

34+
// Expose the following methods to MapProxy by overriding them (so that classes in this package see them)
35+
36+
@Override
37+
protected void prepareForPossibleLoadingOutsideTransaction() {
38+
super.prepareForPossibleLoadingOutsideTransaction();
39+
}
40+
41+
@Override
42+
protected boolean isAllowLoadOutsideTransaction() {
43+
return super.isAllowLoadOutsideTransaction();
44+
}
45+
46+
@Override
47+
protected String getSessionFactoryUuid() {
48+
return super.getSessionFactoryUuid();
49+
}
3450
}

hibernate-core/src/main/java/org/hibernate/proxy/map/MapProxy.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,12 @@ public class MapProxy implements HibernateProxy, Map, Serializable {
2222

2323
private MapLazyInitializer li;
2424

25+
private Object replacement;
26+
2527
MapProxy(MapLazyInitializer li) {
2628
this.li = li;
2729
}
2830

29-
public Object writeReplace() {
30-
return this;
31-
}
32-
3331
public LazyInitializer getHibernateLazyInitializer() {
3432
return li;
3533
}
@@ -82,4 +80,34 @@ public Object put(Object key, Object value) {
8280
return li.getMap().put(key, value);
8381
}
8482

83+
@Override
84+
public Object writeReplace() {
85+
/*
86+
* If the target has already been loaded somewhere, just not set on the proxy,
87+
* then use it to initialize the proxy so that we will serialize that instead of the proxy.
88+
*/
89+
li.initializeWithoutLoadIfPossible();
90+
91+
if ( li.isUninitialized() ) {
92+
if ( replacement == null ) {
93+
li.prepareForPossibleLoadingOutsideTransaction();
94+
replacement = serializableProxy();
95+
}
96+
return replacement;
97+
}
98+
else {
99+
return li.getImplementation();
100+
}
101+
}
102+
103+
private Object serializableProxy() {
104+
return new SerializableMapProxy(
105+
li.getEntityName(),
106+
li.getIdentifier(),
107+
( li.isReadOnlySettingAvailable() ? Boolean.valueOf( li.isReadOnly() ) : li.isReadOnlyBeforeAttachedToSession() ),
108+
li.getSessionFactoryUuid(),
109+
li.isAllowLoadOutsideTransaction()
110+
);
111+
}
112+
85113
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.proxy.map;
8+
9+
import java.io.Serializable;
10+
import java.lang.reflect.Method;
11+
12+
import org.hibernate.proxy.AbstractSerializableProxy;
13+
import org.hibernate.proxy.HibernateProxy;
14+
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor;
15+
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory;
16+
import org.hibernate.type.CompositeType;
17+
18+
public final class SerializableMapProxy extends AbstractSerializableProxy {
19+
20+
public SerializableMapProxy(
21+
String entityName,
22+
Serializable id,
23+
Boolean readOnly,
24+
String sessionFactoryUuid,
25+
boolean allowLoadOutsideTransaction) {
26+
super( entityName, id, readOnly, sessionFactoryUuid, allowLoadOutsideTransaction );
27+
}
28+
29+
private Object readResolve() {
30+
MapLazyInitializer initializer = new MapLazyInitializer( getEntityName(), getId(), null );
31+
afterDeserialization( initializer );
32+
return new MapProxy( initializer );
33+
}
34+
}

hibernate-core/src/test/java/org/hibernate/serialization/MapProxySerializationTest.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.hibernate.proxy.HibernateProxy;
2222
import org.hibernate.proxy.map.MapProxy;
2323

24-
import org.hibernate.testing.FailureExpected;
2524
import org.hibernate.testing.TestForIssue;
2625
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
2726
import org.junit.Before;
@@ -86,7 +85,6 @@ public void prepare() {
8685
@SuppressWarnings("unchecked")
8786
@Test
8887
@TestForIssue(jiraKey = "HHH-7686")
89-
@FailureExpected(jiraKey = "HHH-7686")
9088
public void testInitializedProxySerializationIfTargetInPersistenceContext() {
9189
final Session s = openSession();
9290

@@ -128,7 +126,6 @@ public void testInitializedProxySerializationIfTargetInPersistenceContext() {
128126
@SuppressWarnings("unchecked")
129127
@Test
130128
@TestForIssue(jiraKey = "HHH-7686")
131-
@FailureExpected(jiraKey = "HHH-7686")
132129
public void testUninitializedProxySerializationIfTargetInPersistenceContext() {
133130
final Session s = openSession();
134131

@@ -210,7 +207,6 @@ public void testProxyInitializationWithoutTX() {
210207
@SuppressWarnings("unchecked")
211208
@Test
212209
@TestForIssue(jiraKey = "HHH-7686")
213-
@FailureExpected(jiraKey = "HHH-7686")
214210
public void testProxyInitializationWithoutTXAfterDeserialization() {
215211
final Session s = openSession();
216212

0 commit comments

Comments
 (0)