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:
parent
bc6c982541
commit
782336faed
|
@ -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 + "]"
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue