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.
This commit is contained in:
Yoann Rodière 2018-07-04 13:05:45 +02:00 committed by Guillaume Smet
parent bc6c982541
commit 782336faed
6 changed files with 95 additions and 12 deletions

View File

@ -45,8 +45,17 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
private boolean allowLoadOutsideTransaction;
/**
* For serialization from the non-pojo initializers (HHH-3309)
* @deprecated This constructor was initially intended for serialization only, and is not useful anymore.
* In any case it should not be relied on by user code.
* Subclasses should rather implement Serializable with an {@code Object writeReplace()} method returning
* a subclass of {@link AbstractSerializableProxy},
* which in turn implements Serializable and an {@code Object readResolve()} method
* instantiating the {@link AbstractLazyInitializer} subclass
* and calling {@link AbstractSerializableProxy#afterDeserialization(AbstractLazyInitializer)} on it.
* See {@link org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor} and
* {@link org.hibernate.proxy.pojo.bytebuddy.SerializableProxy} for examples.
*/
@Deprecated
protected AbstractLazyInitializer() {
}
@ -240,7 +249,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
* and the entity being proxied has been loaded and added to the persistence context
* of that session since the proxy was created.
*/
protected final void initializeWithoutLoadIfPossible() {
public final void initializeWithoutLoadIfPossible() {
if ( !initialized && session != null && session.isOpen() ) {
final EntityKey key = session.generateEntityKey(
getIdentifier(),
@ -380,7 +389,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
*
* @throws IllegalStateException if isReadOnlySettingAvailable() == true
*/
protected final Boolean isReadOnlyBeforeAttachedToSession() {
public final Boolean isReadOnlyBeforeAttachedToSession() {
if ( isReadOnlySettingAvailable() ) {
throw new IllegalStateException(
"Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"

View File

@ -9,7 +9,7 @@ package org.hibernate.proxy;
import java.io.Serializable;
/**
* Convenience base class for SerializableProxy.
* Convenience base class for the serialized form of {@link AbstractLazyInitializer}.
*
* @author Gail Badner
*/

View File

@ -31,4 +31,20 @@ public class MapLazyInitializer extends AbstractLazyInitializer implements Seria
throw new UnsupportedOperationException("dynamic-map entity representation");
}
// Expose the following methods to MapProxy by overriding them (so that classes in this package see them)
@Override
protected void prepareForPossibleLoadingOutsideTransaction() {
super.prepareForPossibleLoadingOutsideTransaction();
}
@Override
protected boolean isAllowLoadOutsideTransaction() {
return super.isAllowLoadOutsideTransaction();
}
@Override
protected String getSessionFactoryUuid() {
return super.getSessionFactoryUuid();
}
}

View File

@ -22,14 +22,12 @@ public class MapProxy implements HibernateProxy, Map, Serializable {
private MapLazyInitializer li;
private Object replacement;
MapProxy(MapLazyInitializer li) {
this.li = li;
}
public Object writeReplace() {
return this;
}
public LazyInitializer getHibernateLazyInitializer() {
return li;
}
@ -82,4 +80,34 @@ public class MapProxy implements HibernateProxy, Map, Serializable {
return li.getMap().put(key, value);
}
@Override
public Object writeReplace() {
/*
* If the target has already been loaded somewhere, just not set on the proxy,
* then use it to initialize the proxy so that we will serialize that instead of the proxy.
*/
li.initializeWithoutLoadIfPossible();
if ( li.isUninitialized() ) {
if ( replacement == null ) {
li.prepareForPossibleLoadingOutsideTransaction();
replacement = serializableProxy();
}
return replacement;
}
else {
return li.getImplementation();
}
}
private Object serializableProxy() {
return new SerializableMapProxy(
li.getEntityName(),
li.getIdentifier(),
( li.isReadOnlySettingAvailable() ? Boolean.valueOf( li.isReadOnly() ) : li.isReadOnlyBeforeAttachedToSession() ),
li.getSessionFactoryUuid(),
li.isAllowLoadOutsideTransaction()
);
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.proxy.map;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.hibernate.proxy.AbstractSerializableProxy;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor;
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory;
import org.hibernate.type.CompositeType;
public final class SerializableMapProxy extends AbstractSerializableProxy {
public SerializableMapProxy(
String entityName,
Serializable id,
Boolean readOnly,
String sessionFactoryUuid,
boolean allowLoadOutsideTransaction) {
super( entityName, id, readOnly, sessionFactoryUuid, allowLoadOutsideTransaction );
}
private Object readResolve() {
MapLazyInitializer initializer = new MapLazyInitializer( getEntityName(), getId(), null );
afterDeserialization( initializer );
return new MapProxy( initializer );
}
}

View File

@ -21,7 +21,6 @@ import org.hibernate.proxy.AbstractLazyInitializer;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.map.MapProxy;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Before;
@ -86,7 +85,6 @@ public class MapProxySerializationTest extends BaseCoreFunctionalTestCase {
@SuppressWarnings("unchecked")
@Test
@TestForIssue(jiraKey = "HHH-7686")
@FailureExpected(jiraKey = "HHH-7686")
public void testInitializedProxySerializationIfTargetInPersistenceContext() {
final Session s = openSession();
@ -128,7 +126,6 @@ public class MapProxySerializationTest extends BaseCoreFunctionalTestCase {
@SuppressWarnings("unchecked")
@Test
@TestForIssue(jiraKey = "HHH-7686")
@FailureExpected(jiraKey = "HHH-7686")
public void testUninitializedProxySerializationIfTargetInPersistenceContext() {
final Session s = openSession();
@ -210,7 +207,6 @@ public class MapProxySerializationTest extends BaseCoreFunctionalTestCase {
@SuppressWarnings("unchecked")
@Test
@TestForIssue(jiraKey = "HHH-7686")
@FailureExpected(jiraKey = "HHH-7686")
public void testProxyInitializationWithoutTXAfterDeserialization() {
final Session s = openSession();