HHH-11147 - Allow enhanced entities to be returned in a completely uninitialized state
This commit is contained in:
parent
e4a3586316
commit
5d518cb673
|
@ -8,6 +8,7 @@ package org.hibernate;
|
|||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.HibernateIterator;
|
||||
|
@ -63,6 +64,13 @@ public final class Hibernate {
|
|||
else if ( proxy instanceof PersistentCollection ) {
|
||||
( (PersistentCollection) proxy ).forceInitialization();
|
||||
}
|
||||
else if ( proxy instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) proxy;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( proxy, null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,6 +84,13 @@ public final class Hibernate {
|
|||
if ( proxy instanceof HibernateProxy ) {
|
||||
return !( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUninitialized();
|
||||
}
|
||||
else if ( proxy instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) proxy ).$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if ( proxy instanceof PersistentCollection ) {
|
||||
return ( (PersistentCollection) proxy ).wasInitialized();
|
||||
}
|
||||
|
@ -187,7 +202,10 @@ public final class Hibernate {
|
|||
|
||||
if ( entity instanceof PersistentAttributeInterceptable ) {
|
||||
PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
|
||||
if ( interceptor != null && interceptor instanceof LazyAttributeLoadingInterceptor ) {
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
return false;
|
||||
}
|
||||
if ( interceptor instanceof LazyAttributeLoadingInterceptor ) {
|
||||
return ( (LazyAttributeLoadingInterceptor) interceptor ).isAttributeLoaded( propertyName );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,9 +63,8 @@ import org.hibernate.service.spi.ServiceRegistryImplementor;
|
|||
import org.hibernate.tuple.entity.EntityTuplizer;
|
||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.cfg.AvailableSettings.ACQUIRE_CONNECTIONS;
|
||||
import static org.hibernate.cfg.AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY;
|
||||
import static org.hibernate.cfg.AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS;
|
||||
import static org.hibernate.cfg.AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY;
|
||||
import static org.hibernate.cfg.AvailableSettings.ALLOW_UPDATE_OUTSIDE_TRANSACTION;
|
||||
|
@ -193,6 +192,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
private NullPrecedence defaultNullPrecedence;
|
||||
private boolean orderUpdatesEnabled;
|
||||
private boolean orderInsertsEnabled;
|
||||
private boolean enhancementAsProxyEnabled;
|
||||
|
||||
|
||||
// multi-tenancy
|
||||
|
@ -244,6 +244,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
|
||||
private boolean nativeExceptionHandling51Compliance;
|
||||
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "deprecation"})
|
||||
public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, BootstrapContext context) {
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
|
@ -340,6 +341,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
this.defaultNullPrecedence = NullPrecedence.parse( defaultNullPrecedence );
|
||||
this.orderUpdatesEnabled = ConfigurationHelper.getBoolean( ORDER_UPDATES, configurationSettings );
|
||||
this.orderInsertsEnabled = ConfigurationHelper.getBoolean( ORDER_INSERTS, configurationSettings );
|
||||
this.enhancementAsProxyEnabled = ConfigurationHelper.getBoolean( ALLOW_ENHANCEMENT_AS_PROXY, configurationSettings );
|
||||
|
||||
this.jtaTrackByThread = cfgService.getSetting( JTA_TRACK_BY_THREAD, BOOLEAN, true );
|
||||
|
||||
|
@ -1031,6 +1033,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
return nativeExceptionHandling51Compliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnhancementAsProxyEnabled() {
|
||||
return enhancementAsProxyEnabled;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// In-flight mutation access
|
||||
|
||||
|
|
|
@ -432,4 +432,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
|
|||
public boolean nativeExceptionHandling51Compliance() {
|
||||
return delegate.nativeExceptionHandling51Compliance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnhancementAsProxyEnabled() {
|
||||
return delegate.isEnhancementAsProxyEnabled();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,4 +290,11 @@ public interface SessionFactoryOptions {
|
|||
default boolean nativeExceptionHandling51Compliance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can bytecode-enhanced entity classes be used as a "proxy"?
|
||||
*/
|
||||
default boolean isEnhancementAsProxyEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.bytecode;
|
||||
|
||||
import org.jboss.logging.BasicLogger;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface BytecodeLogger extends BasicLogger {
|
||||
String NAME = "org.hibernate.orm.bytecode";
|
||||
|
||||
Logger LOGGER = Logger.getLogger( NAME );
|
||||
|
||||
boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
|
||||
boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
|
||||
}
|
|
@ -459,6 +459,11 @@ public class EnhancerImpl implements Enhancer {
|
|||
return getAnnotations().isAnnotationPresent( annotationType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return fieldDescription.toString();
|
||||
}
|
||||
|
||||
<T extends Annotation> AnnotationDescription.Loadable<T> getAnnotation(Class<T> annotationType) {
|
||||
return getAnnotations().ofType( annotationType );
|
||||
}
|
||||
|
|
|
@ -101,7 +101,10 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
|
|||
}
|
||||
TypeDefinition managedCtSuperclass = managedCtClass.getSuperClass();
|
||||
|
||||
if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass.asErasure() ) ) {
|
||||
if ( enhancementContext.isEntityClass( managedCtSuperclass.asErasure() ) ) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
else if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass.asErasure() ) ) {
|
||||
return collectInheritPersistentFields( managedCtSuperclass, enhancementContext );
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
package org.hibernate.bytecode.enhance.spi;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
|
@ -32,9 +34,18 @@ public interface LazyPropertyInitializer {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Prefer the form of these methods defined on
|
||||
* {@link BytecodeLazyAttributeInterceptor} instead
|
||||
*/
|
||||
@Deprecated
|
||||
interface InterceptorImplementor {
|
||||
Set<String> getInitializedLazyAttributeNames();
|
||||
void attributeInitialized(String name);
|
||||
default Set<String> getInitializedLazyAttributeNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
default void attributeInitialized(String name) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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.bytecode.enhance.spi.interceptor;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractInterceptor implements SessionAssociableInterceptor {
|
||||
private final String entityName;
|
||||
|
||||
private transient SharedSessionContractImplementor session;
|
||||
private boolean allowLoadOutsideTransaction;
|
||||
private String sessionFactoryUuid;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public AbstractInterceptor(String entityName) {
|
||||
this.entityName = entityName;
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedSessionContractImplementor getLinkedSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSession(SharedSessionContractImplementor session) {
|
||||
this.session = session;
|
||||
if ( session != null && !allowLoadOutsideTransaction ) {
|
||||
this.allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
|
||||
if ( this.allowLoadOutsideTransaction ) {
|
||||
this.sessionFactoryUuid = session.getFactory().getUuid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsetSession() {
|
||||
this.session = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowLoadOutsideTransaction() {
|
||||
return allowLoadOutsideTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSessionFactoryUuid() {
|
||||
return sessionFactoryUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case of reading an attribute. The result is what is returned to the caller
|
||||
*/
|
||||
protected abstract Object handleRead(Object target, String attributeName, Object value);
|
||||
|
||||
/**
|
||||
* Handle the case of writing an attribute. The result is what is set as the entity state
|
||||
*/
|
||||
protected abstract Object handleWrite(Object target, String attributeName, Object oldValue, Object newValue);
|
||||
|
||||
@Override
|
||||
public boolean readBoolean(Object obj, String name, boolean oldValue) {
|
||||
return (Boolean) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) {
|
||||
return (Boolean) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte(Object obj, String name, byte oldValue) {
|
||||
return (Byte) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte writeByte(Object obj, String name, byte oldValue, byte newValue) {
|
||||
return (Byte) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar(Object obj, String name, char oldValue) {
|
||||
return (Character) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public char writeChar(Object obj, String name, char oldValue, char newValue) {
|
||||
return (char) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort(Object obj, String name, short oldValue) {
|
||||
return (Short) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public short writeShort(Object obj, String name, short oldValue, short newValue) {
|
||||
return (Short) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt(Object obj, String name, int oldValue) {
|
||||
return (Integer) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeInt(Object obj, String name, int oldValue, int newValue) {
|
||||
return (Integer) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat(Object obj, String name, float oldValue) {
|
||||
return (Float) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public float writeFloat(Object obj, String name, float oldValue, float newValue) {
|
||||
return (Float) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble(Object obj, String name, double oldValue) {
|
||||
return (Double) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public double writeDouble(Object obj, String name, double oldValue, double newValue) {
|
||||
return (Double) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong(Object obj, String name, long oldValue) {
|
||||
return (Long) handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public long writeLong(Object obj, String name, long oldValue, long newValue) {
|
||||
return (Long) handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readObject(Object obj, String name, Object oldValue) {
|
||||
return handleRead( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
|
||||
return handleWrite( obj, name, oldValue, newValue );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.bytecode.enhance.spi.interceptor;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractLazyLoadInterceptor extends AbstractInterceptor implements BytecodeLazyAttributeInterceptor {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public AbstractLazyLoadInterceptor(String entityName) {
|
||||
super( entityName );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public AbstractLazyLoadInterceptor(String entityName, SharedSessionContractImplementor session) {
|
||||
super( entityName );
|
||||
setSession( session );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.bytecode.enhance.spi.interceptor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Incubating
|
||||
public interface BytecodeLazyAttributeInterceptor extends SessionAssociableInterceptor {
|
||||
/**
|
||||
* The name of the entity this interceptor is meant to intercept
|
||||
*/
|
||||
String getEntityName();
|
||||
|
||||
/**
|
||||
* The id of the entity instance this interceptor is associated with
|
||||
*/
|
||||
Object getIdentifier();
|
||||
|
||||
/**
|
||||
* The names of all lazy attributes which have been initialized
|
||||
*/
|
||||
@Override
|
||||
Set<String> getInitializedLazyAttributeNames();
|
||||
|
||||
/**
|
||||
* Callback from the enhanced class that an attribute has been read or written
|
||||
*/
|
||||
void attributeInitialized(String name);
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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.bytecode.enhance.spi.interceptor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.bytecode.BytecodeLogger;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.tuple.entity.EntityTuplizer;
|
||||
import org.hibernate.type.CompositeType;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInterceptor {
|
||||
private final Set<String> identifierAttributeNames;
|
||||
private final CompositeType nonAggregatedCidMapper;
|
||||
|
||||
private final EntityKey entityKey;
|
||||
|
||||
private final boolean inLineDirtyChecking;
|
||||
private Set<String> writtenFieldNames;
|
||||
|
||||
private boolean initialized;
|
||||
|
||||
public EnhancementAsProxyLazinessInterceptor(
|
||||
String entityName,
|
||||
Set<String> identifierAttributeNames,
|
||||
CompositeType nonAggregatedCidMapper,
|
||||
EntityKey entityKey,
|
||||
SharedSessionContractImplementor session) {
|
||||
super( entityName, session );
|
||||
|
||||
this.identifierAttributeNames = identifierAttributeNames;
|
||||
assert identifierAttributeNames != null;
|
||||
|
||||
this.nonAggregatedCidMapper = nonAggregatedCidMapper;
|
||||
assert nonAggregatedCidMapper != null || identifierAttributeNames.size() == 1;
|
||||
|
||||
this.entityKey = entityKey;
|
||||
|
||||
final EntityPersister entityPersister = session.getFactory().getMetamodel().entityPersister( entityName );
|
||||
this.inLineDirtyChecking = entityPersister.getEntityMode() == EntityMode.POJO
|
||||
&& SelfDirtinessTracker.class.isAssignableFrom( entityPersister.getMappedClass() );
|
||||
}
|
||||
|
||||
public EntityKey getEntityKey() {
|
||||
return entityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRead(Object target, String attributeName, Object value) {
|
||||
// it is illegal for this interceptor to still be attached to the entity after initialization
|
||||
if ( initialized ) {
|
||||
throw new IllegalStateException( "EnhancementAsProxyLazinessInterceptor interception on an initialized instance" );
|
||||
}
|
||||
|
||||
// the attribute being read is an entity-id attribute
|
||||
// - we already know the id, return that
|
||||
if ( identifierAttributeNames.contains( attributeName ) ) {
|
||||
return extractIdValue( target, attributeName );
|
||||
}
|
||||
|
||||
// Use `performWork` to group together multiple Session accesses
|
||||
return EnhancementHelper.performWork(
|
||||
this,
|
||||
(session, isTempSession) -> {
|
||||
final Object[] writtenValues;
|
||||
|
||||
final EntityPersister entityPersister = session.getFactory()
|
||||
.getMetamodel()
|
||||
.entityPersister( getEntityName() );
|
||||
final EntityTuplizer entityTuplizer = entityPersister.getEntityTuplizer();
|
||||
|
||||
if ( inLineDirtyChecking && writtenFieldNames != null && !writtenFieldNames.isEmpty() ) {
|
||||
|
||||
// enhancement has dirty-tracking available and at least one attribute was explicitly set
|
||||
|
||||
if ( writtenFieldNames.contains( attributeName ) ) {
|
||||
// the requested attribute was one of the attributes explicitly set, we can just return the explicitly set value
|
||||
return entityTuplizer.getPropertyValue( target, attributeName );
|
||||
}
|
||||
|
||||
// otherwise we want to save all of the explicitly set values in anticipation of
|
||||
// the force initialization below so that we can "replay" them after the
|
||||
// initialization
|
||||
|
||||
writtenValues = new Object[writtenFieldNames.size()];
|
||||
|
||||
int index = 0;
|
||||
for ( String writtenFieldName : writtenFieldNames ) {
|
||||
writtenValues[index] = entityTuplizer.getPropertyValue( target, writtenFieldName );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
writtenValues = null;
|
||||
}
|
||||
|
||||
final Object initializedValue = forceInitialize(
|
||||
target,
|
||||
attributeName,
|
||||
session,
|
||||
isTempSession
|
||||
);
|
||||
|
||||
initialized = true;
|
||||
|
||||
if ( writtenValues != null ) {
|
||||
// here is the replaying of the explicitly set values we prepared above
|
||||
int index = 0;
|
||||
for ( String writtenFieldName : writtenFieldNames ) {
|
||||
entityTuplizer.setPropertyValue( target, writtenFieldName, writtenValues[index++] );
|
||||
}
|
||||
writtenFieldNames.clear();
|
||||
}
|
||||
|
||||
return initializedValue;
|
||||
},
|
||||
getEntityName(),
|
||||
attributeName
|
||||
);
|
||||
}
|
||||
|
||||
private Object extractIdValue(Object target, String attributeName) {
|
||||
// access to the id or part of it for non-aggregated cid
|
||||
if ( nonAggregatedCidMapper == null ) {
|
||||
return getIdentifier();
|
||||
}
|
||||
else {
|
||||
return nonAggregatedCidMapper.getPropertyValue(
|
||||
target,
|
||||
nonAggregatedCidMapper.getPropertyIndex( attributeName ),
|
||||
getLinkedSession()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Object forceInitialize(Object target, String attributeName) {
|
||||
BytecodeLogger.LOGGER.tracef(
|
||||
"EnhancementAsProxyLazinessInterceptor#forceInitialize : %s#%s -> %s )",
|
||||
entityKey.getEntityName(),
|
||||
entityKey.getIdentifier(),
|
||||
attributeName
|
||||
);
|
||||
|
||||
return EnhancementHelper.performWork(
|
||||
this,
|
||||
(session, isTemporarySession) -> forceInitialize( target, attributeName, session, isTemporarySession ),
|
||||
getEntityName(),
|
||||
attributeName
|
||||
);
|
||||
}
|
||||
|
||||
public Object forceInitialize(Object target, String attributeName, SharedSessionContractImplementor session, boolean isTemporarySession) {
|
||||
BytecodeLogger.LOGGER.tracef(
|
||||
"EnhancementAsProxyLazinessInterceptor#forceInitialize : %s#%s -> %s )",
|
||||
entityKey.getEntityName(),
|
||||
entityKey.getIdentifier(),
|
||||
attributeName
|
||||
);
|
||||
|
||||
final EntityPersister persister = session.getFactory()
|
||||
.getMetamodel()
|
||||
.entityPersister( getEntityName() );
|
||||
|
||||
if ( isTemporarySession ) {
|
||||
// Add an entry for this entity in the PC of the temp Session
|
||||
session.getPersistenceContext().addEntity(
|
||||
target,
|
||||
Status.READ_ONLY,
|
||||
// loaded state
|
||||
ArrayHelper.filledArray(
|
||||
LazyPropertyInitializer.UNFETCHED_PROPERTY,
|
||||
Object.class,
|
||||
persister.getPropertyTypes().length
|
||||
),
|
||||
entityKey,
|
||||
persister.getVersion( target ),
|
||||
LockMode.NONE,
|
||||
// we assume an entry exists in the db
|
||||
true,
|
||||
persister,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return persister.initializeEnhancedEntityUsedAsProxy(
|
||||
target,
|
||||
attributeName,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleWrite(Object target, String attributeName, Object oldValue, Object newValue) {
|
||||
if ( initialized ) {
|
||||
throw new IllegalStateException( "EnhancementAsProxyLazinessInterceptor interception on an initialized instance" );
|
||||
}
|
||||
|
||||
if ( identifierAttributeNames.contains( attributeName ) ) {
|
||||
EnhancementHelper.performWork(
|
||||
this,
|
||||
(session, isTempSession) -> session.getFactory()
|
||||
.getMetamodel()
|
||||
.entityPersister( getEntityName() )
|
||||
.getEntityTuplizer()
|
||||
.getPropertyValue( target, attributeName ),
|
||||
getEntityName(),
|
||||
attributeName
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! inLineDirtyChecking ) {
|
||||
// we need to force-initialize the proxy - the fetch group to which the `attributeName` belongs
|
||||
try {
|
||||
forceInitialize( target, attributeName );
|
||||
}
|
||||
finally {
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// because of the entity being enhanced with `org.hibernate.engine.spi.SelfDirtinessTracker`
|
||||
// we can skip forcing the initialization. However, in the case of a subsequent read we
|
||||
// need to know which attributes had been explicitly set so that we can re-play the setters
|
||||
// after the force-initialization there
|
||||
if ( writtenFieldNames == null ) {
|
||||
writtenFieldNames = new HashSet<>();
|
||||
}
|
||||
writtenFieldNames.add( attributeName );
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getInitializedLazyAttributeNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attributeInitialized(String name) {
|
||||
if ( initialized ) {
|
||||
throw new UnsupportedOperationException( "Expected call to EnhancementAsProxyLazinessInterceptor#attributeInitialized" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier() {
|
||||
return entityKey.getIdentifier();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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.bytecode.enhance.spi.interceptor;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.LazyInitializationException;
|
||||
import org.hibernate.bytecode.BytecodeLogger;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.SessionFactoryRegistry;
|
||||
import org.hibernate.mapping.OneToOne;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.mapping.Value;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EnhancementHelper {
|
||||
/**
|
||||
* Should the given property be included in the owner's base fetch group?
|
||||
*/
|
||||
public static boolean includeInBaseFetchGroup(
|
||||
Property bootMapping,
|
||||
boolean isEnhanced,
|
||||
boolean allowEnhancementAsProxy,
|
||||
Function<String,Boolean> hasSubclassChecker) {
|
||||
final Value value = bootMapping.getValue();
|
||||
|
||||
if ( ! isEnhanced ) {
|
||||
if ( value instanceof ToOne ) {
|
||||
if ( ( (ToOne) value ).isUnwrapProxy() ) {
|
||||
BytecodeLogger.LOGGER.debugf(
|
||||
"To-one property `%s#%s` was mapped with LAZY + NO_PROXY but the class was not enhanced",
|
||||
bootMapping.getPersistentClass().getEntityName(),
|
||||
bootMapping.getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value instanceof ToOne ) {
|
||||
final ToOne toOne = (ToOne) value;
|
||||
if ( toOne.isLazy() ) {
|
||||
if ( toOne.isUnwrapProxy() ) {
|
||||
if ( toOne instanceof OneToOne ) {
|
||||
return false;
|
||||
}
|
||||
// include it in the base fetch group so long as the config allows
|
||||
// using the FK to create an "enhancement proxy"
|
||||
// return allowEnhancementAsProxy && hasSubclassChecker.apply( toOne.getReferencedEntityName() );
|
||||
return allowEnhancementAsProxy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! bootMapping.isLazy();
|
||||
}
|
||||
|
||||
public static <T> T performWork(
|
||||
BytecodeLazyAttributeInterceptor interceptor,
|
||||
BiFunction<SharedSessionContractImplementor, Boolean, T> work,
|
||||
String entityName,
|
||||
String attributeName) {
|
||||
SharedSessionContractImplementor session = interceptor.getLinkedSession();
|
||||
|
||||
boolean isTempSession = false;
|
||||
boolean isJta = false;
|
||||
|
||||
// first figure out which Session to use
|
||||
if ( session == null ) {
|
||||
if ( interceptor.allowLoadOutsideTransaction() ) {
|
||||
session = openTemporarySessionForLoading( interceptor, entityName, attributeName );
|
||||
isTempSession = true;
|
||||
}
|
||||
else {
|
||||
throwLazyInitializationException( Cause.NO_SESSION, entityName, attributeName );
|
||||
}
|
||||
}
|
||||
else if ( !session.isOpen() ) {
|
||||
if ( interceptor.allowLoadOutsideTransaction() ) {
|
||||
session = openTemporarySessionForLoading( interceptor, entityName, attributeName );
|
||||
isTempSession = true;
|
||||
}
|
||||
else {
|
||||
throwLazyInitializationException( Cause.CLOSED_SESSION, entityName, attributeName );
|
||||
}
|
||||
}
|
||||
else if ( !session.isConnected() ) {
|
||||
if ( interceptor.allowLoadOutsideTransaction() ) {
|
||||
session = openTemporarySessionForLoading( interceptor, entityName, attributeName );
|
||||
isTempSession = true;
|
||||
}
|
||||
else {
|
||||
throwLazyInitializationException( Cause.DISCONNECTED_SESSION, entityName, attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
// If we are using a temporary Session, begin a transaction if necessary
|
||||
if ( isTempSession ) {
|
||||
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork started temporary Session" );
|
||||
|
||||
isJta = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
|
||||
|
||||
if ( !isJta ) {
|
||||
// Explicitly handle the transactions only if we're not in
|
||||
// a JTA environment. A lazy loading temporary session can
|
||||
// be created even if a current session and transaction are
|
||||
// open (ex: session.clear() was used). We must prevent
|
||||
// multiple transactions.
|
||||
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork starting transaction on temporary Session" );
|
||||
session.beginTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// do the actual work
|
||||
return work.apply( session, isTempSession );
|
||||
}
|
||||
finally {
|
||||
if ( isTempSession ) {
|
||||
try {
|
||||
// Commit the JDBC transaction is we started one.
|
||||
if ( !isJta ) {
|
||||
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork committing transaction on temporary Session" );
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
BytecodeLogger.LOGGER.warn(
|
||||
"Unable to commit JDBC transaction on temporary session used to load lazy " +
|
||||
"collection associated to no session"
|
||||
);
|
||||
}
|
||||
|
||||
// Close the just opened temp Session
|
||||
try {
|
||||
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork closing temporary Session" );
|
||||
session.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
BytecodeLogger.LOGGER.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Cause {
|
||||
NO_SESSION,
|
||||
CLOSED_SESSION,
|
||||
DISCONNECTED_SESSION,
|
||||
NO_SF_UUID
|
||||
}
|
||||
|
||||
private static void throwLazyInitializationException(Cause cause, String entityName, String attributeName) {
|
||||
final String reason;
|
||||
switch ( cause ) {
|
||||
case NO_SESSION: {
|
||||
reason = "no session and settings disallow loading outside the Session";
|
||||
break;
|
||||
}
|
||||
case CLOSED_SESSION: {
|
||||
reason = "session is closed and settings disallow loading outside the Session";
|
||||
break;
|
||||
}
|
||||
case DISCONNECTED_SESSION: {
|
||||
reason = "session is disconnected and settings disallow loading outside the Session";
|
||||
break;
|
||||
}
|
||||
case NO_SF_UUID: {
|
||||
reason = "could not determine SessionFactory UUId to create temporary Session for loading";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
reason = "<should never get here>";
|
||||
}
|
||||
}
|
||||
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"Unable to perform requested lazy initialization [%s.%s] - %s",
|
||||
entityName,
|
||||
attributeName,
|
||||
reason
|
||||
);
|
||||
|
||||
throw new LazyInitializationException( message );
|
||||
}
|
||||
|
||||
private static SharedSessionContractImplementor openTemporarySessionForLoading(
|
||||
BytecodeLazyAttributeInterceptor interceptor,
|
||||
String entityName,
|
||||
String attributeName) {
|
||||
if ( interceptor.getSessionFactoryUuid() == null ) {
|
||||
throwLazyInitializationException( Cause.NO_SF_UUID, entityName, attributeName );
|
||||
}
|
||||
|
||||
final SessionFactoryImplementor sf = (SessionFactoryImplementor)
|
||||
SessionFactoryRegistry.INSTANCE.getSessionFactory( interceptor.getSessionFactoryUuid() );
|
||||
final SharedSessionContractImplementor session = (SharedSessionContractImplementor) sf.openSession();
|
||||
session.getPersistenceContext().setDefaultReadOnly( true );
|
||||
session.setHibernateFlushMode( FlushMode.MANUAL );
|
||||
return session;
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* 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.bytecode.enhance.spi.interceptor;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.LazyInitializationException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.SessionFactoryRegistry;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Helper {
|
||||
private static final Logger log = Logger.getLogger( Helper.class );
|
||||
|
||||
interface Consumer {
|
||||
SharedSessionContractImplementor getLinkedSession();
|
||||
boolean allowLoadOutsideTransaction();
|
||||
String getSessionFactoryUuid();
|
||||
}
|
||||
|
||||
interface LazyInitializationWork<T> {
|
||||
T doWork(SharedSessionContractImplementor session, boolean isTemporarySession);
|
||||
|
||||
// informational details
|
||||
String getEntityName();
|
||||
String getAttributeName();
|
||||
}
|
||||
|
||||
|
||||
private final Consumer consumer;
|
||||
|
||||
public Helper(Consumer consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public <T> T performWork(LazyInitializationWork<T> lazyInitializationWork) {
|
||||
SharedSessionContractImplementor session = consumer.getLinkedSession();
|
||||
|
||||
boolean isTempSession = false;
|
||||
boolean isJta = false;
|
||||
|
||||
// first figure out which Session to use
|
||||
if ( session == null ) {
|
||||
if ( consumer.allowLoadOutsideTransaction() ) {
|
||||
session = openTemporarySessionForLoading( lazyInitializationWork );
|
||||
isTempSession = true;
|
||||
}
|
||||
else {
|
||||
throwLazyInitializationException( Cause.NO_SESSION, lazyInitializationWork );
|
||||
}
|
||||
}
|
||||
else if ( !session.isOpen() ) {
|
||||
if ( consumer.allowLoadOutsideTransaction() ) {
|
||||
session = openTemporarySessionForLoading( lazyInitializationWork );
|
||||
isTempSession = true;
|
||||
}
|
||||
else {
|
||||
throwLazyInitializationException( Cause.CLOSED_SESSION, lazyInitializationWork );
|
||||
}
|
||||
}
|
||||
else if ( !session.isConnected() ) {
|
||||
if ( consumer.allowLoadOutsideTransaction() ) {
|
||||
session = openTemporarySessionForLoading( lazyInitializationWork );
|
||||
isTempSession = true;
|
||||
}
|
||||
else {
|
||||
throwLazyInitializationException( Cause.DISCONNECTED_SESSION, lazyInitializationWork );
|
||||
}
|
||||
}
|
||||
|
||||
// If we are using a temporary Session, begin a transaction if necessary
|
||||
if ( isTempSession ) {
|
||||
isJta = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
|
||||
|
||||
if ( !isJta ) {
|
||||
// Explicitly handle the transactions only if we're not in
|
||||
// a JTA environment. A lazy loading temporary session can
|
||||
// be created even if a current session and transaction are
|
||||
// open (ex: session.clear() was used). We must prevent
|
||||
// multiple transactions.
|
||||
session.beginTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// do the actual work
|
||||
return lazyInitializationWork.doWork( session, isTempSession );
|
||||
}
|
||||
finally {
|
||||
if ( isTempSession ) {
|
||||
try {
|
||||
// Commit the JDBC transaction is we started one.
|
||||
if ( !isJta ) {
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn(
|
||||
"Unable to commit JDBC transaction on temporary session used to load lazy " +
|
||||
"collection associated to no session"
|
||||
);
|
||||
}
|
||||
|
||||
// Close the just opened temp Session
|
||||
try {
|
||||
session.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Cause {
|
||||
NO_SESSION,
|
||||
CLOSED_SESSION,
|
||||
DISCONNECTED_SESSION,
|
||||
NO_SF_UUID
|
||||
}
|
||||
|
||||
private void throwLazyInitializationException(Cause cause, LazyInitializationWork work) {
|
||||
final String reason;
|
||||
switch ( cause ) {
|
||||
case NO_SESSION: {
|
||||
reason = "no session and settings disallow loading outside the Session";
|
||||
break;
|
||||
}
|
||||
case CLOSED_SESSION: {
|
||||
reason = "session is closed and settings disallow loading outside the Session";
|
||||
break;
|
||||
}
|
||||
case DISCONNECTED_SESSION: {
|
||||
reason = "session is disconnected and settings disallow loading outside the Session";
|
||||
break;
|
||||
}
|
||||
case NO_SF_UUID: {
|
||||
reason = "could not determine SessionFactory UUId to create temporary Session for loading";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
reason = "<should never get here>";
|
||||
}
|
||||
}
|
||||
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"Unable to perform requested lazy initialization [%s.%s] - %s",
|
||||
work.getEntityName(),
|
||||
work.getAttributeName(),
|
||||
reason
|
||||
);
|
||||
|
||||
throw new LazyInitializationException( message );
|
||||
}
|
||||
|
||||
private SharedSessionContractImplementor openTemporarySessionForLoading(LazyInitializationWork lazyInitializationWork) {
|
||||
if ( consumer.getSessionFactoryUuid() == null ) {
|
||||
throwLazyInitializationException( Cause.NO_SF_UUID, lazyInitializationWork );
|
||||
}
|
||||
|
||||
final SessionFactoryImplementor sf = (SessionFactoryImplementor)
|
||||
SessionFactoryRegistry.INSTANCE.getSessionFactory( consumer.getSessionFactoryUuid() );
|
||||
final SharedSessionContractImplementor session = (SharedSessionContractImplementor) sf.openSession();
|
||||
session.getPersistenceContext().setDefaultReadOnly( true );
|
||||
session.setHibernateFlushMode( FlushMode.MANUAL );
|
||||
return session;
|
||||
}
|
||||
}
|
|
@ -16,47 +16,39 @@ import java.util.Set;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.InterceptorImplementor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.Helper.Consumer;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.Helper.LazyInitializationWork;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Interceptor that loads attributes lazily
|
||||
*
|
||||
* @author Luis Barreiro
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class LazyAttributeLoadingInterceptor
|
||||
implements PersistentAttributeInterceptor, Consumer, InterceptorImplementor {
|
||||
private static final Logger log = Logger.getLogger( LazyAttributeLoadingInterceptor.class );
|
||||
|
||||
private final String entityName;
|
||||
public class LazyAttributeLoadingInterceptor extends AbstractLazyLoadInterceptor {
|
||||
private final Object identifier;
|
||||
private final Set<String> lazyFields;
|
||||
|
||||
private Set<String> initializedLazyFields;
|
||||
|
||||
private transient SharedSessionContractImplementor session;
|
||||
private boolean allowLoadOutsideTransaction;
|
||||
private String sessionFactoryUuid;
|
||||
|
||||
public LazyAttributeLoadingInterceptor(
|
||||
String entityName,
|
||||
Object identifier,
|
||||
Set<String> lazyFields,
|
||||
SharedSessionContractImplementor session) {
|
||||
this.entityName = entityName;
|
||||
super( entityName, session );
|
||||
this.identifier = identifier;
|
||||
this.lazyFields = lazyFields;
|
||||
|
||||
setSession( session );
|
||||
}
|
||||
|
||||
protected final Object intercept(Object target, String attributeName, Object value) {
|
||||
@Override
|
||||
public Object getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRead(Object target, String attributeName, Object value) {
|
||||
if ( !isAttributeLoaded( attributeName ) ) {
|
||||
Object loadedValue = fetchAttribute( target, attributeName );
|
||||
attributeInitialized( attributeName );
|
||||
|
@ -65,6 +57,14 @@ public class LazyAttributeLoadingInterceptor
|
|||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleWrite(Object target, String attributeName, Object oldValue, Object newValue) {
|
||||
if ( !isAttributeLoaded( attributeName ) ) {
|
||||
attributeInitialized( attributeName );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the lazy attribute. The attribute does not get associated with the entity. (To be used by hibernate methods)
|
||||
*/
|
||||
|
@ -73,72 +73,48 @@ public class LazyAttributeLoadingInterceptor
|
|||
}
|
||||
|
||||
protected Object loadAttribute(final Object target, final String attributeName) {
|
||||
return new Helper( this ).performWork(
|
||||
new LazyInitializationWork() {
|
||||
@Override
|
||||
public Object doWork(SharedSessionContractImplementor session, boolean isTemporarySession) {
|
||||
final EntityPersister persister = session.getFactory().getMetamodel().entityPersister( getEntityName() );
|
||||
return EnhancementHelper.performWork(
|
||||
this,
|
||||
(session, isTemporarySession) -> {
|
||||
final EntityPersister persister = session.getFactory().getMetamodel().entityPersister( getEntityName() );
|
||||
|
||||
if ( isTemporarySession ) {
|
||||
final Serializable id = persister.getIdentifier( target, null );
|
||||
if ( isTemporarySession ) {
|
||||
final Serializable id = persister.getIdentifier( target, null );
|
||||
|
||||
// Add an entry for this entity in the PC of the temp Session
|
||||
// NOTE : a few arguments that would be nice to pass along here...
|
||||
// 1) loadedState if we know any
|
||||
final Object[] loadedState = null;
|
||||
// 2) does a row exist in the db for this entity?
|
||||
final boolean existsInDb = true;
|
||||
session.getPersistenceContext().addEntity(
|
||||
target,
|
||||
Status.READ_ONLY,
|
||||
loadedState,
|
||||
session.generateEntityKey( id, persister ),
|
||||
persister.getVersion( target ),
|
||||
LockMode.NONE,
|
||||
existsInDb,
|
||||
persister,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
final LazyPropertyInitializer initializer = (LazyPropertyInitializer) persister;
|
||||
final Object loadedValue = initializer.initializeLazyProperty(
|
||||
attributeName,
|
||||
// Add an entry for this entity in the PC of the temp Session
|
||||
// NOTE : a few arguments that would be nice to pass along here...
|
||||
// 1) loadedState if we know any
|
||||
final Object[] loadedState = null;
|
||||
// 2) does a row exist in the db for this entity?
|
||||
final boolean existsInDb = true;
|
||||
session.getPersistenceContext().addEntity(
|
||||
target,
|
||||
session
|
||||
Status.READ_ONLY,
|
||||
loadedState,
|
||||
session.generateEntityKey( id, persister ),
|
||||
persister.getVersion( target ),
|
||||
LockMode.NONE,
|
||||
existsInDb,
|
||||
persister,
|
||||
true
|
||||
);
|
||||
|
||||
takeCollectionSizeSnapshot( target, attributeName, loadedValue );
|
||||
return loadedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
final LazyPropertyInitializer initializer = (LazyPropertyInitializer) persister;
|
||||
final Object loadedValue = initializer.initializeLazyProperty(
|
||||
attributeName,
|
||||
target,
|
||||
session
|
||||
);
|
||||
|
||||
@Override
|
||||
public String getAttributeName() {
|
||||
return attributeName;
|
||||
}
|
||||
}
|
||||
takeCollectionSizeSnapshot( target, attributeName, loadedValue );
|
||||
return loadedValue;
|
||||
},
|
||||
getEntityName(),
|
||||
attributeName
|
||||
);
|
||||
}
|
||||
|
||||
public final void setSession(SharedSessionContractImplementor session) {
|
||||
this.session = session;
|
||||
if ( session != null && !allowLoadOutsideTransaction ) {
|
||||
this.allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
|
||||
if ( this.allowLoadOutsideTransaction ) {
|
||||
this.sessionFactoryUuid = session.getFactory().getUuid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void unsetSession() {
|
||||
this.session = null;
|
||||
}
|
||||
|
||||
public boolean isAttributeLoaded(String fieldName) {
|
||||
return !isLazyAttribute( fieldName ) || isInitializedLazyField( fieldName );
|
||||
}
|
||||
|
@ -171,13 +147,11 @@ public class LazyAttributeLoadingInterceptor
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LazyAttributeLoader(entityName=" + entityName + " ,lazyFields=" + lazyFields + ')';
|
||||
return "LazyAttributeLoader(entityName=" + getEntityName() + " ,lazyFields=" + lazyFields + ')';
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
private void takeCollectionSizeSnapshot(Object target, String fieldName, Object value) {
|
||||
if ( value != null && value instanceof Collection && target instanceof SelfDirtinessTracker ) {
|
||||
if ( value instanceof Collection && target instanceof SelfDirtinessTracker ) {
|
||||
CollectionTracker tracker = ( (SelfDirtinessTracker) target ).$$_hibernate_getCollectionTracker();
|
||||
if ( tracker == null ) {
|
||||
( (SelfDirtinessTracker) target ).$$_hibernate_clearDirtyAttributes();
|
||||
|
@ -187,152 +161,20 @@ public class LazyAttributeLoadingInterceptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean(Object obj, String name, boolean oldValue) {
|
||||
return (Boolean) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte(Object obj, String name, byte oldValue) {
|
||||
return (Byte) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte writeByte(Object obj, String name, byte oldValue, byte newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar(Object obj, String name, char oldValue) {
|
||||
return (Character) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public char writeChar(Object obj, String name, char oldValue, char newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort(Object obj, String name, short oldValue) {
|
||||
return (Short) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public short writeShort(Object obj, String name, short oldValue, short newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt(Object obj, String name, int oldValue) {
|
||||
return (Integer) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeInt(Object obj, String name, int oldValue, int newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat(Object obj, String name, float oldValue) {
|
||||
return (Float) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public float writeFloat(Object obj, String name, float oldValue, float newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble(Object obj, String name, double oldValue) {
|
||||
return (Double) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public double writeDouble(Object obj, String name, double oldValue, double newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong(Object obj, String name, long oldValue) {
|
||||
return (Long) intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public long writeLong(Object obj, String name, long oldValue, long newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readObject(Object obj, String name, Object oldValue) {
|
||||
return intercept( obj, name, oldValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
|
||||
if ( lazyFields != null && lazyFields.contains( name ) ) {
|
||||
attributeInitialized( name );
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedSessionContractImplementor getLinkedSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowLoadOutsideTransaction() {
|
||||
return allowLoadOutsideTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSessionFactoryUuid() {
|
||||
return sessionFactoryUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attributeInitialized(String name) {
|
||||
if ( !isLazyAttribute( name ) ) {
|
||||
return;
|
||||
}
|
||||
if ( initializedLazyFields == null ) {
|
||||
initializedLazyFields = new HashSet<String>();
|
||||
initializedLazyFields = new HashSet<>();
|
||||
}
|
||||
initializedLazyFields.add( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getInitializedLazyAttributeNames() {
|
||||
return initializedLazyFields == null ? Collections.<String>emptySet() : initializedLazyFields;
|
||||
return initializedLazyFields == null ? Collections.emptySet() : initializedLazyFields;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
|
@ -29,12 +30,12 @@ public class LazyAttributesMetadata implements Serializable {
|
|||
/**
|
||||
* Build a LazyFetchGroupMetadata based on the attributes defined for the
|
||||
* PersistentClass
|
||||
*
|
||||
* @param mappedEntity The entity definition
|
||||
*
|
||||
* @return The built LazyFetchGroupMetadata
|
||||
*/
|
||||
public static LazyAttributesMetadata from(PersistentClass mappedEntity) {
|
||||
public static LazyAttributesMetadata from(
|
||||
PersistentClass mappedEntity,
|
||||
boolean isEnhanced,
|
||||
boolean allowEnhancementAsProxy,
|
||||
Function<String,Boolean> hasSubclassChecker) {
|
||||
final Map<String, LazyAttributeDescriptor> lazyAttributeDescriptorMap = new LinkedHashMap<>();
|
||||
final Map<String, Set<String>> fetchGroupToAttributesMap = new HashMap<>();
|
||||
|
||||
|
@ -44,7 +45,13 @@ public class LazyAttributesMetadata implements Serializable {
|
|||
while ( itr.hasNext() ) {
|
||||
i++;
|
||||
final Property property = (Property) itr.next();
|
||||
if ( property.isLazy() ) {
|
||||
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
|
||||
property,
|
||||
isEnhanced,
|
||||
allowEnhancementAsProxy,
|
||||
hasSubclassChecker
|
||||
);
|
||||
if ( lazy ) {
|
||||
final LazyAttributeDescriptor lazyAttributeDescriptor = LazyAttributeDescriptor.from( property, i, x++ );
|
||||
lazyAttributeDescriptorMap.put( lazyAttributeDescriptor.getName(), lazyAttributeDescriptor );
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.bytecode.enhance.spi.interceptor;
|
||||
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface SessionAssociableInterceptor extends PersistentAttributeInterceptor {
|
||||
SharedSessionContractImplementor getLinkedSession();
|
||||
|
||||
void setSession(SharedSessionContractImplementor session);
|
||||
|
||||
void unsetSession();
|
||||
|
||||
boolean allowLoadOutsideTransaction();
|
||||
|
||||
String getSessionFactoryUuid();
|
||||
}
|
|
@ -6,8 +6,11 @@
|
|||
*/
|
||||
package org.hibernate.bytecode.spi;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
|
@ -37,6 +40,7 @@ public interface BytecodeEnhancementMetadata {
|
|||
* Build and inject an interceptor instance into the enhanced entity.
|
||||
*
|
||||
* @param entity The entity into which built interceptor should be injected
|
||||
* @param identifier
|
||||
* @param session The session to which the entity instance belongs.
|
||||
*
|
||||
* @return The built and injected interceptor
|
||||
|
@ -45,8 +49,19 @@ public interface BytecodeEnhancementMetadata {
|
|||
*/
|
||||
LazyAttributeLoadingInterceptor injectInterceptor(
|
||||
Object entity,
|
||||
Object identifier,
|
||||
SharedSessionContractImplementor session) throws NotInstrumentedException;
|
||||
|
||||
void injectInterceptor(
|
||||
Object entity,
|
||||
PersistentAttributeInterceptor interceptor,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
void injectEnhancedEntityAsProxyInterceptor(
|
||||
Object entity,
|
||||
EntityKey entityKey,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Extract the field interceptor instance from the enhanced entity.
|
||||
*
|
||||
|
@ -58,6 +73,8 @@ public interface BytecodeEnhancementMetadata {
|
|||
*/
|
||||
LazyAttributeLoadingInterceptor extractInterceptor(Object entity) throws NotInstrumentedException;
|
||||
|
||||
BytecodeLazyAttributeInterceptor extractLazyInterceptor(Object entity) throws NotInstrumentedException;
|
||||
|
||||
boolean hasUnFetchedAttributes(Object entity);
|
||||
|
||||
boolean isAttributeLoaded(Object entity, String attributeName);
|
||||
|
|
|
@ -859,6 +859,27 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
*/
|
||||
String ENFORCE_LEGACY_PROXY_CLASSNAMES = "hibernate.bytecode.enforce_legacy_proxy_classnames";
|
||||
|
||||
/**
|
||||
* Should Hibernate use enhanced entities "as a proxy"?
|
||||
*
|
||||
* E.g., when an application uses {@link org.hibernate.Session#load} against an enhanced
|
||||
* class, enabling this will allow Hibernate to create an "empty" instance of the enhanced
|
||||
* class to act as the proxy - it contains just the identifier which is later used to
|
||||
* trigger the base initialization but no other data is loaded
|
||||
*
|
||||
* Not enabling this (the legacy default behavior) would cause the "base" attributes to
|
||||
* be loaded. Any lazy-group attributes would not be initialized.
|
||||
*
|
||||
* Applications using bytecode enhancement and switching to allowing this should be careful
|
||||
* in use of the various {@link org.hibernate.Hibernate} methods such as
|
||||
* {@link org.hibernate.Hibernate#isInitialized},
|
||||
* {@link org.hibernate.Hibernate#isPropertyInitialized}, etc - enabling this setting changes
|
||||
* the results of those methods
|
||||
*
|
||||
* @implSpec See {@link org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor}
|
||||
*/
|
||||
String ALLOW_ENHANCEMENT_AS_PROXY = "hibernate.bytecode.allow_enhancement_as_proxy";
|
||||
|
||||
/**
|
||||
* The classname of the HQL query parser factory
|
||||
*/
|
||||
|
|
|
@ -16,12 +16,15 @@ import org.hibernate.EntityMode;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityEntryExtraState;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -345,6 +348,15 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
|
|||
return ! persister.hasCollections() && ! ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes();
|
||||
}
|
||||
|
||||
if ( entity instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) entity;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
// we never have to check an uninitialized proxy
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
|
||||
getPersistenceContext().getSession().getFactory().getCustomEntityDirtinessStrategy();
|
||||
if ( customEntityDirtinessStrategy.canDirtyCheck( entity, getPersister(), (Session) getPersistenceContext().getSession() ) ) {
|
||||
|
|
|
@ -94,7 +94,7 @@ public final class Cascade {
|
|||
final String propertyName = propertyNames[ i ];
|
||||
final boolean isUninitializedProperty =
|
||||
hasUninitializedLazyProperties &&
|
||||
!persister.getInstrumentationMetadata().isAttributeLoaded( parent, propertyName );
|
||||
!persister.getBytecodeEnhancementMetadata().isAttributeLoaded( parent, propertyName );
|
||||
|
||||
if ( style.doCascade( action ) ) {
|
||||
final Object child;
|
||||
|
@ -133,7 +133,7 @@ public final class Cascade {
|
|||
else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) {
|
||||
// Only need to initialize a lazy entity attribute when action.performOnLazyProperty()
|
||||
// returns true.
|
||||
LazyAttributeLoadingInterceptor interceptor = persister.getInstrumentationMetadata()
|
||||
LazyAttributeLoadingInterceptor interceptor = persister.getBytecodeEnhancementMetadata()
|
||||
.extractInterceptor( parent );
|
||||
child = interceptor.fetchAttribute( parent, propertyName );
|
||||
|
||||
|
|
|
@ -164,9 +164,10 @@ public final class Collections {
|
|||
//TODO: better to pass the id in as an argument?
|
||||
ce.setCurrentKey( type.getKeyOfOwner( entity, session ) );
|
||||
|
||||
final boolean isBytecodeEnhanced = persister.getOwnerEntityPersister().getInstrumentationMetadata().isEnhancedForLazyLoading();
|
||||
final boolean isBytecodeEnhanced = persister.getOwnerEntityPersister().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
|
||||
if ( isBytecodeEnhanced && !collection.wasInitialized() ) {
|
||||
// skip it
|
||||
// the class of the collection owner is enhanced for lazy loading and we found an un-initialized PersistentCollection
|
||||
// - skip it
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf(
|
||||
"Skipping uninitialized bytecode-lazy collection: %s",
|
||||
|
@ -175,57 +176,58 @@ public final class Collections {
|
|||
}
|
||||
ce.setReached( true );
|
||||
ce.setProcessed( true );
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// The CollectionEntry.isReached() stuff is just to detect any silly users
|
||||
// who set up circular or shared references between/to collections.
|
||||
if ( ce.isReached() ) {
|
||||
// We've been here before
|
||||
throw new HibernateException(
|
||||
"Found shared references to a collection: " + type.getRole()
|
||||
|
||||
// The CollectionEntry.isReached() stuff is just to detect any silly users
|
||||
// who set up circular or shared references between/to collections.
|
||||
if ( ce.isReached() ) {
|
||||
// We've been here before
|
||||
throw new HibernateException(
|
||||
"Found shared references to a collection: " + type.getRole()
|
||||
);
|
||||
}
|
||||
|
||||
ce.setReached( true );
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
if ( collection.wasInitialized() ) {
|
||||
LOG.debugf(
|
||||
"Collection found: %s, was: %s (initialized)",
|
||||
MessageHelper.collectionInfoString(
|
||||
persister,
|
||||
collection,
|
||||
ce.getCurrentKey(),
|
||||
session
|
||||
),
|
||||
MessageHelper.collectionInfoString(
|
||||
ce.getLoadedPersister(),
|
||||
collection,
|
||||
ce.getLoadedKey(),
|
||||
session
|
||||
)
|
||||
);
|
||||
}
|
||||
ce.setReached( true );
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
if ( collection.wasInitialized() ) {
|
||||
LOG.debugf(
|
||||
"Collection found: %s, was: %s (initialized)",
|
||||
MessageHelper.collectionInfoString(
|
||||
persister,
|
||||
collection,
|
||||
ce.getCurrentKey(),
|
||||
session
|
||||
),
|
||||
MessageHelper.collectionInfoString(
|
||||
ce.getLoadedPersister(),
|
||||
collection,
|
||||
ce.getLoadedKey(),
|
||||
session
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
LOG.debugf(
|
||||
"Collection found: %s, was: %s (uninitialized)",
|
||||
MessageHelper.collectionInfoString(
|
||||
persister,
|
||||
collection,
|
||||
ce.getCurrentKey(),
|
||||
session
|
||||
),
|
||||
MessageHelper.collectionInfoString(
|
||||
ce.getLoadedPersister(),
|
||||
collection,
|
||||
ce.getLoadedKey(),
|
||||
session
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
LOG.debugf(
|
||||
"Collection found: %s, was: %s (uninitialized)",
|
||||
MessageHelper.collectionInfoString(
|
||||
persister,
|
||||
collection,
|
||||
ce.getCurrentKey(),
|
||||
session
|
||||
),
|
||||
MessageHelper.collectionInfoString(
|
||||
ce.getLoadedPersister(),
|
||||
collection,
|
||||
ce.getLoadedKey(),
|
||||
session
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
prepareCollectionForUpdate( collection, ce, factory );
|
||||
}
|
||||
|
||||
prepareCollectionForUpdate( collection, ce, factory );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -177,10 +177,7 @@ public final class ForeignKeys {
|
|||
// superclass or the same as the entity type of a nullifiable entity).
|
||||
// It is unclear if a more complicated check would impact performance
|
||||
// more than just initializing the associated entity.
|
||||
return persister
|
||||
.getInstrumentationMetadata()
|
||||
.extractInterceptor( self )
|
||||
.fetchAttribute( self, propertyName );
|
||||
return ( (LazyPropertyInitializer) persister ).initializeLazyProperty( propertyName, self, session );
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
|
|
|
@ -31,6 +31,8 @@ import org.hibernate.NonUniqueObjectException;
|
|||
import org.hibernate.PersistentObjectException;
|
||||
import org.hibernate.TransientObjectException;
|
||||
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
|
@ -495,6 +497,8 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
final boolean existsInDatabase,
|
||||
final EntityPersister persister,
|
||||
final boolean disableVersionIncrement) {
|
||||
assert lockMode != null;
|
||||
|
||||
final EntityEntry e;
|
||||
|
||||
/*
|
||||
|
@ -571,15 +575,29 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public boolean reassociateIfUninitializedProxy(Object value) throws MappingException {
|
||||
if ( !Hibernate.isInitialized( value ) ) {
|
||||
final HibernateProxy proxy = (HibernateProxy) value;
|
||||
final LazyInitializer li = proxy.getHibernateLazyInitializer();
|
||||
reassociateProxy( li, proxy );
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
if ( ! Hibernate.isInitialized( value ) ) {
|
||||
|
||||
// could be a proxy....
|
||||
if ( value instanceof HibernateProxy ) {
|
||||
final HibernateProxy proxy = (HibernateProxy) value;
|
||||
final LazyInitializer li = proxy.getHibernateLazyInitializer();
|
||||
reassociateProxy( li, proxy );
|
||||
return true;
|
||||
}
|
||||
|
||||
// or an uninitialized enhanced entity ("bytecode proxy")...
|
||||
if ( value instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptable bytecodeProxy = (PersistentAttributeInterceptable) value;
|
||||
final BytecodeLazyAttributeInterceptor interceptor = (BytecodeLazyAttributeInterceptor) bytecodeProxy.$$_hibernate_getInterceptor();
|
||||
if ( interceptor != null ) {
|
||||
interceptor.setSession( getSession() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -636,6 +654,14 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
//initialize + unwrap the object and return it
|
||||
return li.getImplementation();
|
||||
}
|
||||
else if ( maybeProxy instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) maybeProxy;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( maybeProxy, null );
|
||||
}
|
||||
return maybeProxy;
|
||||
}
|
||||
else {
|
||||
return maybeProxy;
|
||||
}
|
||||
|
|
|
@ -341,7 +341,7 @@ public final class TwoPhaseLoad {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -256,7 +256,7 @@ public class CollectionLoadContext {
|
|||
// If the owner is bytecode-enhanced and the owner's collection value is uninitialized,
|
||||
// then go ahead and set it to the newly initialized collection.
|
||||
final BytecodeEnhancementMetadata bytecodeEnhancementMetadata =
|
||||
persister.getOwnerEntityPersister().getInstrumentationMetadata();
|
||||
persister.getOwnerEntityPersister().getBytecodeEnhancementMetadata();
|
||||
if ( bytecodeEnhancementMetadata.isEnhancedForLazyLoading() ) {
|
||||
// Lazy properties in embeddables/composites are not currently supported for embeddables (HHH-10480),
|
||||
// so check to make sure the collection is not in an embeddable before checking to see if
|
||||
|
|
|
@ -6,47 +6,76 @@
|
|||
*/
|
||||
package org.hibernate.engine.spi;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.InterceptorImplementor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
|
||||
/**
|
||||
* The base contract for interceptors that can be injected into
|
||||
* enhanced entities for the purpose of intercepting attribute access
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see PersistentAttributeInterceptable
|
||||
*/
|
||||
@Incubating
|
||||
@SuppressWarnings("unused")
|
||||
public interface PersistentAttributeInterceptor extends InterceptorImplementor {
|
||||
boolean readBoolean(Object obj, String name, boolean oldValue);
|
||||
|
||||
public boolean readBoolean(Object obj, String name, boolean oldValue);
|
||||
boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue);
|
||||
|
||||
public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue);
|
||||
byte readByte(Object obj, String name, byte oldValue);
|
||||
|
||||
public byte readByte(Object obj, String name, byte oldValue);
|
||||
byte writeByte(Object obj, String name, byte oldValue, byte newValue);
|
||||
|
||||
public byte writeByte(Object obj, String name, byte oldValue, byte newValue);
|
||||
char readChar(Object obj, String name, char oldValue);
|
||||
|
||||
public char readChar(Object obj, String name, char oldValue);
|
||||
char writeChar(Object obj, String name, char oldValue, char newValue);
|
||||
|
||||
public char writeChar(Object obj, String name, char oldValue, char newValue);
|
||||
short readShort(Object obj, String name, short oldValue);
|
||||
|
||||
public short readShort(Object obj, String name, short oldValue);
|
||||
short writeShort(Object obj, String name, short oldValue, short newValue);
|
||||
|
||||
public short writeShort(Object obj, String name, short oldValue, short newValue);
|
||||
int readInt(Object obj, String name, int oldValue);
|
||||
|
||||
public int readInt(Object obj, String name, int oldValue);
|
||||
int writeInt(Object obj, String name, int oldValue, int newValue);
|
||||
|
||||
public int writeInt(Object obj, String name, int oldValue, int newValue);
|
||||
float readFloat(Object obj, String name, float oldValue);
|
||||
|
||||
public float readFloat(Object obj, String name, float oldValue);
|
||||
float writeFloat(Object obj, String name, float oldValue, float newValue);
|
||||
|
||||
public float writeFloat(Object obj, String name, float oldValue, float newValue);
|
||||
double readDouble(Object obj, String name, double oldValue);
|
||||
|
||||
public double readDouble(Object obj, String name, double oldValue);
|
||||
double writeDouble(Object obj, String name, double oldValue, double newValue);
|
||||
|
||||
public double writeDouble(Object obj, String name, double oldValue, double newValue);
|
||||
long readLong(Object obj, String name, long oldValue);
|
||||
|
||||
public long readLong(Object obj, String name, long oldValue);
|
||||
long writeLong(Object obj, String name, long oldValue, long newValue);
|
||||
|
||||
public long writeLong(Object obj, String name, long oldValue, long newValue);
|
||||
Object readObject(Object obj, String name, Object oldValue);
|
||||
|
||||
public Object readObject(Object obj, String name, Object oldValue);
|
||||
Object writeObject(Object obj, String name, Object oldValue, Object newValue);
|
||||
|
||||
public Object writeObject(Object obj, String name, Object oldValue, Object newValue);
|
||||
/**
|
||||
* @deprecated Just as the method it overrides. Interceptors that deal with
|
||||
* lazy state should implement {@link BytecodeLazyAttributeInterceptor}
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
default Set<String> getInitializedLazyAttributeNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Just as the method it overrides. Interceptors that deal with
|
||||
* lazy state should implement {@link BytecodeLazyAttributeInterceptor}
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
default void attributeInitialized(String name) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,12 +246,22 @@ public interface SharedSessionContractImplementor
|
|||
Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable)
|
||||
throws HibernateException;
|
||||
|
||||
default Object internalLoad(
|
||||
String entityName,
|
||||
Serializable id,
|
||||
boolean eager,
|
||||
boolean nullable,
|
||||
Boolean unwrapProxy) throws HibernateException {
|
||||
return internalLoad( entityName, id, eager, nullable );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an instance immediately. This method is only called when lazily initializing a proxy.
|
||||
* Do not return the proxy.
|
||||
*/
|
||||
Object immediateLoad(String entityName, Serializable id) throws HibernateException;
|
||||
|
||||
|
||||
/**
|
||||
* Execute a <tt>find()</tt> query
|
||||
*/
|
||||
|
|
|
@ -363,7 +363,7 @@ public abstract class AbstractSaveEventListener
|
|||
Object[] values,
|
||||
Type[] types,
|
||||
EventSource source) {
|
||||
WrapVisitor visitor = new WrapVisitor( source );
|
||||
WrapVisitor visitor = new WrapVisitor( entity, id, source );
|
||||
// substitutes into values by side-effect
|
||||
visitor.processEntityPropertyValues( values, types );
|
||||
return visitor.isSubstitutionRequired();
|
||||
|
|
|
@ -69,8 +69,6 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
* Handle the given load event.
|
||||
*
|
||||
* @param event The load event to be handled.
|
||||
*
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void onLoad(
|
||||
final LoadEvent event,
|
||||
|
@ -102,7 +100,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
);
|
||||
}
|
||||
else {
|
||||
return event.getSession().getFactory().getEntityPersister( event.getEntityClassName() );
|
||||
return event.getSession().getFactory().getMetamodel().entityPersister( event.getEntityClassName() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +155,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
loadType,
|
||||
persister,
|
||||
dependentIdType,
|
||||
event.getSession().getFactory().getEntityPersister( dependentParentType.getAssociatedEntityName() )
|
||||
event.getSession().getFactory().getMetamodel().entityPersister( dependentParentType.getAssociatedEntityName() )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -196,8 +194,6 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
* @param options The defined load options
|
||||
*
|
||||
* @return The loaded entity.
|
||||
*
|
||||
* @throws HibernateException
|
||||
*/
|
||||
private Object load(
|
||||
final LoadEvent event,
|
||||
|
@ -260,27 +256,108 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
);
|
||||
}
|
||||
|
||||
// this class has no proxies (so do a shortcut)
|
||||
if ( !persister.hasProxy() ) {
|
||||
return load( event, persister, keyToLoad, options );
|
||||
}
|
||||
|
||||
final PersistenceContext persistenceContext = event.getSession().getPersistenceContext();
|
||||
|
||||
// look for a proxy
|
||||
Object proxy = persistenceContext.getProxy( keyToLoad );
|
||||
if ( proxy != null ) {
|
||||
return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy );
|
||||
}
|
||||
final boolean allowBytecodeProxy = event.getSession()
|
||||
.getFactory()
|
||||
.getSessionFactoryOptions()
|
||||
.isEnhancementAsProxyEnabled();
|
||||
|
||||
if ( options.isAllowProxyCreation() ) {
|
||||
return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext );
|
||||
final boolean entityHasHibernateProxyFactory = persister.getEntityMetamodel()
|
||||
.getTuplizer()
|
||||
.getProxyFactory() != null;
|
||||
|
||||
// Check for the case where we can use the entity itself as a proxy
|
||||
if ( options.isAllowProxyCreation()
|
||||
&& allowBytecodeProxy
|
||||
&& persister.getEntityMetamodel().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
|
||||
// if there is already a managed entity instance associated with the PC, return it
|
||||
final Object managed = persistenceContext.getEntity( keyToLoad );
|
||||
if ( managed != null ) {
|
||||
if ( options.isCheckDeleted() ) {
|
||||
final EntityEntry entry = persistenceContext.getEntry( managed );
|
||||
final Status status = entry.getStatus();
|
||||
if ( status == Status.DELETED || status == Status.GONE ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return managed;
|
||||
}
|
||||
|
||||
// if the entity defines a HibernateProxy factory, see if there is an
|
||||
// existing proxy associated with the PC - and if so, use it
|
||||
if ( entityHasHibernateProxyFactory ) {
|
||||
final Object proxy = persistenceContext.getProxy( keyToLoad );
|
||||
|
||||
if ( proxy != null ) {
|
||||
LOG.trace( "Entity proxy found in session cache" );
|
||||
|
||||
LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
|
||||
|
||||
if ( li.isUnwrap() || event.getShouldUnwrapProxy() ) {
|
||||
return li.getImplementation();
|
||||
}
|
||||
|
||||
|
||||
return persistenceContext.narrowProxy( proxy, persister, keyToLoad, null );
|
||||
}
|
||||
|
||||
// specialized handling for entities with subclasses with a HibernateProxy factory
|
||||
if ( persister.getEntityMetamodel().hasSubclasses() ) {
|
||||
// entities with subclasses that define a ProxyFactory can create
|
||||
// a HibernateProxy so long as NO_PROXY was not specified.
|
||||
if ( event.getShouldUnwrapProxy() != null && event.getShouldUnwrapProxy() ) {
|
||||
LOG.debugf( "Ignoring NO_PROXY for to-one association with subclasses to honor laziness" );
|
||||
}
|
||||
return createProxy( event, persister, keyToLoad, persistenceContext );
|
||||
}
|
||||
}
|
||||
|
||||
// This is the crux of HHH-11147
|
||||
// create the (uninitialized) entity instance - has only id set
|
||||
final Object entity = persister.getEntityTuplizer().instantiate(
|
||||
keyToLoad.getIdentifier(),
|
||||
event.getSession()
|
||||
);
|
||||
|
||||
// add the entity instance to the persistence context
|
||||
persistenceContext.addEntity(
|
||||
entity,
|
||||
Status.MANAGED,
|
||||
null,
|
||||
keyToLoad,
|
||||
null,
|
||||
LockMode.NONE,
|
||||
true,
|
||||
persister,
|
||||
true
|
||||
);
|
||||
|
||||
persister.getEntityMetamodel()
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.injectEnhancedEntityAsProxyInterceptor( entity, keyToLoad, event.getSession() );
|
||||
|
||||
return entity;
|
||||
}
|
||||
else {
|
||||
if ( persister.hasProxy() ) {
|
||||
// look for a proxy
|
||||
Object proxy = persistenceContext.getProxy( keyToLoad );
|
||||
if ( proxy != null ) {
|
||||
return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy );
|
||||
}
|
||||
|
||||
if ( options.isAllowProxyCreation() ) {
|
||||
return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return a newly loaded object
|
||||
return load( event, persister, keyToLoad, options );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a proxy, initialize it and/or narrow it provided either
|
||||
* is necessary.
|
||||
|
@ -304,10 +381,13 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
if ( traceEnabled ) {
|
||||
LOG.trace( "Entity proxy found in session cache" );
|
||||
}
|
||||
|
||||
LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
|
||||
|
||||
if ( li.isUnwrap() ) {
|
||||
return li.getImplementation();
|
||||
}
|
||||
|
||||
Object impl = null;
|
||||
if ( !options.isAllowProxyCreation() ) {
|
||||
impl = load( event, persister, keyToLoad, options );
|
||||
|
@ -318,6 +398,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
.handleEntityNotFound( persister.getEntityName(), keyToLoad.getIdentifier() );
|
||||
}
|
||||
}
|
||||
|
||||
return persistenceContext.narrowProxy( proxy, persister, keyToLoad, impl );
|
||||
}
|
||||
|
||||
|
@ -358,6 +439,14 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
if ( traceEnabled ) {
|
||||
LOG.trace( "Creating new proxy for entity" );
|
||||
}
|
||||
return createProxy( event, persister, keyToLoad, persistenceContext );
|
||||
}
|
||||
|
||||
private Object createProxy(
|
||||
LoadEvent event,
|
||||
EntityPersister persister,
|
||||
EntityKey keyToLoad,
|
||||
PersistenceContext persistenceContext) {
|
||||
// return new uninitialized proxy
|
||||
Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
|
||||
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
|
||||
|
@ -376,8 +465,6 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
* @param source The originating session
|
||||
*
|
||||
* @return The loaded entity
|
||||
*
|
||||
* @throws HibernateException
|
||||
*/
|
||||
private Object lockAndLoad(
|
||||
final LoadEvent event,
|
||||
|
@ -505,6 +592,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
*
|
||||
* @return The object loaded from the datasource, or null if not found.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected Object loadFromDatasource(
|
||||
final LoadEvent event,
|
||||
final EntityPersister persister) {
|
||||
|
|
|
@ -17,12 +17,15 @@ import org.hibernate.WrongClassException;
|
|||
import org.hibernate.boot.registry.selector.spi.StrategySelector;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.engine.internal.Cascade;
|
||||
import org.hibernate.engine.internal.CascadePoint;
|
||||
import org.hibernate.engine.spi.CascadingAction;
|
||||
import org.hibernate.engine.spi.CascadingActions;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
@ -109,26 +112,41 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
|||
final EventSource source = event.getSession();
|
||||
final Object original = event.getOriginal();
|
||||
|
||||
if ( original != null ) {
|
||||
// NOTE : `original` is the value being merged
|
||||
|
||||
if ( original != null ) {
|
||||
final Object entity;
|
||||
if ( original instanceof HibernateProxy ) {
|
||||
LazyInitializer li = ( (HibernateProxy) original ).getHibernateLazyInitializer();
|
||||
if ( li.isUninitialized() ) {
|
||||
LOG.trace( "Ignoring uninitialized proxy" );
|
||||
event.setResult( source.load( li.getEntityName(), li.getIdentifier() ) );
|
||||
return; //EARLY EXIT!
|
||||
//EARLY EXIT!
|
||||
return;
|
||||
}
|
||||
else {
|
||||
entity = li.getImplementation();
|
||||
}
|
||||
}
|
||||
else if ( original instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) original;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
final EnhancementAsProxyLazinessInterceptor proxyInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
|
||||
LOG.trace( "Ignoring uninitialized enhanced-proxy" );
|
||||
event.setResult( source.load( proxyInterceptor.getEntityName(), (Serializable) proxyInterceptor.getIdentifier() ) );
|
||||
//EARLY EXIT!
|
||||
return;
|
||||
}
|
||||
else {
|
||||
entity = original;
|
||||
}
|
||||
}
|
||||
else {
|
||||
entity = original;
|
||||
}
|
||||
|
||||
if ( copyCache.containsKey( entity ) &&
|
||||
( copyCache.isOperatedOn( entity ) ) ) {
|
||||
if ( copyCache.containsKey( entity ) && ( copyCache.isOperatedOn( entity ) ) ) {
|
||||
LOG.trace( "Already in merge process" );
|
||||
event.setResult( entity );
|
||||
}
|
||||
|
@ -210,36 +228,50 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
|||
LOG.trace( "Merging transient instance" );
|
||||
|
||||
final Object entity = event.getEntity();
|
||||
final EventSource source = event.getSession();
|
||||
final EventSource session = event.getSession();
|
||||
|
||||
final String entityName = event.getEntityName();
|
||||
final EntityPersister persister = source.getEntityPersister( entityName, entity );
|
||||
final EntityPersister persister = session.getEntityPersister( entityName, entity );
|
||||
|
||||
final Serializable id = persister.hasIdentifierProperty() ?
|
||||
persister.getIdentifier( entity, source ) :
|
||||
null;
|
||||
if ( copyCache.containsKey( entity ) ) {
|
||||
persister.setIdentifier( copyCache.get( entity ), id, source );
|
||||
final Serializable id = persister.hasIdentifierProperty()
|
||||
? persister.getIdentifier( entity, session )
|
||||
: null;
|
||||
|
||||
final Object copy;
|
||||
final Object existingCopy = copyCache.get( entity );
|
||||
if ( existingCopy != null ) {
|
||||
persister.setIdentifier( copyCache.get( entity ), id, session );
|
||||
copy = existingCopy;
|
||||
}
|
||||
else {
|
||||
( (MergeContext) copyCache ).put( entity, source.instantiate( persister, id ), true ); //before cascade!
|
||||
copy = session.instantiate( persister, id );
|
||||
|
||||
//before cascade!
|
||||
( (MergeContext) copyCache ).put( entity, copy, true );
|
||||
}
|
||||
final Object copy = copyCache.get( entity );
|
||||
|
||||
// cascade first, so that all unsaved objects get their
|
||||
// copy created before we actually copy
|
||||
//cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
|
||||
super.cascadeBeforeSave( source, persister, entity, copyCache );
|
||||
copyValues( persister, entity, copy, source, copyCache, ForeignKeyDirection.FROM_PARENT );
|
||||
super.cascadeBeforeSave( session, persister, entity, copyCache );
|
||||
copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.FROM_PARENT );
|
||||
|
||||
saveTransientEntity( copy, entityName, event.getRequestedId(), source, copyCache );
|
||||
saveTransientEntity( copy, entityName, event.getRequestedId(), session, copyCache );
|
||||
|
||||
// cascade first, so that all unsaved objects get their
|
||||
// copy created before we actually copy
|
||||
super.cascadeAfterSave( source, persister, entity, copyCache );
|
||||
copyValues( persister, entity, copy, source, copyCache, ForeignKeyDirection.TO_PARENT );
|
||||
super.cascadeAfterSave( session, persister, entity, copyCache );
|
||||
copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.TO_PARENT );
|
||||
|
||||
event.setResult( copy );
|
||||
|
||||
if ( copy instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) copy;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor == null ) {
|
||||
persister.getBytecodeEnhancementMetadata().injectInterceptor( copy, id, session );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveTransientEntity(
|
||||
|
@ -283,11 +315,12 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
|||
|
||||
String previousFetchProfile = source.getLoadQueryInfluencers().getInternalFetchProfile();
|
||||
source.getLoadQueryInfluencers().setInternalFetchProfile( "merge" );
|
||||
|
||||
//we must clone embedded composite identifiers, or
|
||||
//we will get back the same instance that we pass in
|
||||
final Serializable clonedIdentifier = (Serializable) persister.getIdentifierType()
|
||||
.deepCopy( id, source.getFactory() );
|
||||
final Serializable clonedIdentifier = (Serializable) persister.getIdentifierType().deepCopy( id, source.getFactory() );
|
||||
final Object result = source.get( entityName, clonedIdentifier );
|
||||
|
||||
source.getLoadQueryInfluencers().setInternalFetchProfile( previousFetchProfile );
|
||||
|
||||
if ( result == null ) {
|
||||
|
@ -301,9 +334,11 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
|||
entityIsTransient( event, copyCache );
|
||||
}
|
||||
else {
|
||||
( (MergeContext) copyCache ).put( entity, result, true ); //before cascade!
|
||||
// before cascade!
|
||||
( (MergeContext) copyCache ).put( entity, result, true );
|
||||
|
||||
final Object target = unproxyManagedForDetachedMerging( entity, result, persister, source );
|
||||
|
||||
final Object target = source.getPersistenceContext().unproxy( result );
|
||||
if ( target == entity ) {
|
||||
throw new AssertionFailure( "entity was not detached" );
|
||||
}
|
||||
|
@ -334,6 +369,43 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
|||
|
||||
}
|
||||
|
||||
private Object unproxyManagedForDetachedMerging(
|
||||
Object incoming,
|
||||
Object managed,
|
||||
EntityPersister persister,
|
||||
EventSource source) {
|
||||
if ( incoming instanceof HibernateProxy ) {
|
||||
return source.getPersistenceContext().unproxy( managed );
|
||||
}
|
||||
|
||||
if ( incoming instanceof PersistentAttributeInterceptable
|
||||
&& persister.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
|
||||
&& source.getSessionFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled() ) {
|
||||
|
||||
final PersistentAttributeInterceptor incomingInterceptor = ( (PersistentAttributeInterceptable) incoming ).$$_hibernate_getInterceptor();
|
||||
final PersistentAttributeInterceptor managedInterceptor = ( (PersistentAttributeInterceptable) managed ).$$_hibernate_getInterceptor();
|
||||
|
||||
// todo - do we need to specially handle the case where both `incoming` and `managed` are initialized, but
|
||||
// with different attributes initialized?
|
||||
// - for now, assume we do not...
|
||||
|
||||
// if the managed entity is not a proxy, we can just return it
|
||||
if ( ! ( managedInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) ) {
|
||||
return managed;
|
||||
}
|
||||
|
||||
// if the incoming entity is still a proxy there is no need to force initialization of the managed one
|
||||
if ( incomingInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
return managed;
|
||||
}
|
||||
|
||||
// otherwise, force initialization
|
||||
persister.initializeEnhancedEntityUsedAsProxy( managed, null, source );
|
||||
}
|
||||
|
||||
return managed;
|
||||
}
|
||||
|
||||
private void markInterceptorDirty(final Object entity, final Object target, EntityPersister persister) {
|
||||
// for enhanced entities, copy over the dirty attributes
|
||||
if ( entity instanceof SelfDirtinessTracker && target instanceof SelfDirtinessTracker ) {
|
||||
|
|
|
@ -21,17 +21,20 @@ import org.hibernate.type.CollectionType;
|
|||
* @author Gavin King
|
||||
*/
|
||||
public class FlushVisitor extends AbstractVisitor {
|
||||
|
||||
private Object owner;
|
||||
|
||||
Object processCollection(Object collection, CollectionType type)
|
||||
throws HibernateException {
|
||||
FlushVisitor(EventSource session, Object owner) {
|
||||
super(session);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
Object processCollection(Object collection, CollectionType type) throws HibernateException {
|
||||
|
||||
if (collection==CollectionType.UNFETCHED_COLLECTION) {
|
||||
if ( collection == CollectionType.UNFETCHED_COLLECTION ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (collection!=null) {
|
||||
if ( collection != null ) {
|
||||
final PersistentCollection coll;
|
||||
if ( type.hasHolder() ) {
|
||||
coll = getSession().getPersistenceContext().getCollectionHolder(collection);
|
||||
|
@ -55,9 +58,4 @@ public class FlushVisitor extends AbstractVisitor {
|
|||
return true;
|
||||
}
|
||||
|
||||
FlushVisitor(EventSource session, Object owner) {
|
||||
super(session);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
*/
|
||||
package org.hibernate.event.internal;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
@ -25,10 +28,19 @@ import org.hibernate.type.Type;
|
|||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class WrapVisitor extends ProxyVisitor {
|
||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( WrapVisitor.class );
|
||||
private Object entity;
|
||||
private Serializable id;
|
||||
|
||||
boolean substitute;
|
||||
private boolean substitute;
|
||||
|
||||
public WrapVisitor(Object entity, Serializable id, EventSource session) {
|
||||
super( session );
|
||||
this.entity = entity;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
boolean isSubstitutionRequired() {
|
||||
return substitute;
|
||||
|
@ -42,20 +54,26 @@ public class WrapVisitor extends ProxyVisitor {
|
|||
Object processCollection(Object collection, CollectionType collectionType)
|
||||
throws HibernateException {
|
||||
|
||||
if ( collection != null && ( collection instanceof PersistentCollection ) ) {
|
||||
if ( collection == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( collection == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( collection instanceof PersistentCollection ) {
|
||||
final PersistentCollection coll = (PersistentCollection) collection;
|
||||
final SessionImplementor session = getSession();
|
||||
PersistentCollection coll = (PersistentCollection) collection;
|
||||
|
||||
if ( coll.setCurrentSession( session ) ) {
|
||||
reattachCollection( coll, collectionType );
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
else {
|
||||
return processArrayOrNewCollection( collection, collectionType );
|
||||
}
|
||||
|
||||
return processArrayOrNewCollection( collection, collectionType );
|
||||
}
|
||||
|
||||
final Object processArrayOrNewCollection(Object collection, CollectionType collectionType)
|
||||
|
|
|
@ -47,6 +47,8 @@ public class LoadEvent extends AbstractEvent {
|
|||
private Object result;
|
||||
private PostLoadEvent postLoadEvent;
|
||||
|
||||
private Boolean shouldUnwrapProxy;
|
||||
|
||||
public LoadEvent(Serializable entityId, Object instanceToLoad, EventSource source) {
|
||||
this( entityId, null, instanceToLoad, DEFAULT_LOCK_OPTIONS, false, source );
|
||||
}
|
||||
|
@ -190,4 +192,19 @@ public class LoadEvent extends AbstractEvent {
|
|||
public void setPostLoadEvent(PostLoadEvent postLoadEvent) {
|
||||
this.postLoadEvent = postLoadEvent;
|
||||
}
|
||||
|
||||
public Boolean getShouldUnwrapProxy() {
|
||||
if ( shouldUnwrapProxy == null ) {
|
||||
final boolean enabled = getSession().getFactory()
|
||||
.getSessionFactoryOptions()
|
||||
.isEnhancementAsProxyEnabled();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
return shouldUnwrapProxy;
|
||||
}
|
||||
|
||||
public void setShouldUnwrapProxy(Boolean shouldUnwrapProxy) {
|
||||
this.shouldUnwrapProxy = shouldUnwrapProxy;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.sql.SQLException;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.hql.internal.HolderInstantiator;
|
||||
|
@ -189,21 +190,28 @@ public class ScrollableResultsImpl extends AbstractScrollableResults implements
|
|||
return;
|
||||
}
|
||||
|
||||
final Object result = getLoader().loadSingleRow(
|
||||
getResultSet(),
|
||||
getSession(),
|
||||
getQueryParameters(),
|
||||
true
|
||||
);
|
||||
if ( result != null && result.getClass().isArray() ) {
|
||||
currentRow = (Object[]) result;
|
||||
}
|
||||
else {
|
||||
currentRow = new Object[] {result};
|
||||
}
|
||||
final PersistenceContext persistenceContext = getSession().getPersistenceContext();
|
||||
persistenceContext.beforeLoad();
|
||||
try {
|
||||
final Object result = getLoader().loadSingleRow(
|
||||
getResultSet(),
|
||||
getSession(),
|
||||
getQueryParameters(),
|
||||
true
|
||||
);
|
||||
if ( result != null && result.getClass().isArray() ) {
|
||||
currentRow = (Object[]) result;
|
||||
}
|
||||
else {
|
||||
currentRow = new Object[] {result};
|
||||
}
|
||||
|
||||
if ( getHolderInstantiator() != null ) {
|
||||
currentRow = new Object[] {getHolderInstantiator().instantiate( currentRow )};
|
||||
if ( getHolderInstantiator() != null ) {
|
||||
currentRow = new Object[] { getHolderInstantiator().instantiate( currentRow ) };
|
||||
}
|
||||
}
|
||||
finally {
|
||||
persistenceContext.afterLoad();
|
||||
}
|
||||
|
||||
afterScrollOperation();
|
||||
|
|
|
@ -1123,25 +1123,40 @@ public final class SessionImpl
|
|||
}
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public final Object internalLoad(
|
||||
String entityName,
|
||||
Serializable id,
|
||||
boolean eager,
|
||||
boolean nullable) throws HibernateException {
|
||||
return internalLoad( entityName, id, eager, nullable, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable)
|
||||
throws HibernateException {
|
||||
// todo : remove
|
||||
LoadEventListener.LoadType type = nullable
|
||||
? LoadEventListener.INTERNAL_LOAD_NULLABLE
|
||||
: eager
|
||||
? LoadEventListener.INTERNAL_LOAD_EAGER
|
||||
: LoadEventListener.INTERNAL_LOAD_LAZY;
|
||||
public final Object internalLoad(
|
||||
String entityName,
|
||||
Serializable id,
|
||||
boolean eager,
|
||||
boolean nullable,
|
||||
Boolean unwrapProxy) {
|
||||
final LoadEventListener.LoadType type;
|
||||
if ( nullable ) {
|
||||
type = LoadEventListener.INTERNAL_LOAD_NULLABLE;
|
||||
}
|
||||
else {
|
||||
type = eager
|
||||
? LoadEventListener.INTERNAL_LOAD_EAGER
|
||||
: LoadEventListener.INTERNAL_LOAD_LAZY;
|
||||
}
|
||||
|
||||
LoadEvent event = loadEvent;
|
||||
loadEvent = null;
|
||||
event = recycleEventInstance( event, id, entityName );
|
||||
fireLoad( event, type );
|
||||
event.setShouldUnwrapProxy( unwrapProxy );fireLoad( event, type );
|
||||
Object result = event.getResult();
|
||||
if ( !nullable ) {
|
||||
UnresolvableObjectException.throwIfNull( result, id, entityName );
|
||||
}
|
||||
UnresolvableObjectException.throwIfNull( result, id, entityName );}
|
||||
|
||||
if ( loadEvent == null ) {
|
||||
event.setEntityClassName( null );
|
||||
event.setEntityId( null );
|
||||
|
@ -1166,6 +1181,7 @@ public final class SessionImpl
|
|||
event.setLockMode( LoadEvent.DEFAULT_LOCK_MODE );
|
||||
event.setLockScope( LoadEvent.DEFAULT_LOCK_OPTIONS.getScope() );
|
||||
event.setLockTimeout( LoadEvent.DEFAULT_LOCK_OPTIONS.getTimeOut() );
|
||||
event.setShouldUnwrapProxy( null );
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,13 +65,15 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
}
|
||||
};
|
||||
|
||||
private PersistenceContext temporaryPersistenceContext = new StatefulPersistenceContext( this );
|
||||
private final PersistenceContext temporaryPersistenceContext = new StatefulPersistenceContext( this );
|
||||
|
||||
private boolean connectionProvided;
|
||||
private final boolean connectionProvided;
|
||||
private final boolean allowBytecodeProxy;
|
||||
|
||||
StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) {
|
||||
super( factory, options );
|
||||
connectionProvided = options.getConnection() != null;
|
||||
allowBytecodeProxy = getFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -248,9 +250,12 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object immediateLoad(String entityName, Serializable id)
|
||||
throws HibernateException {
|
||||
throw new SessionException( "proxies cannot be fetched by a stateless session" );
|
||||
public Object immediateLoad(String entityName, Serializable id) throws HibernateException {
|
||||
if ( getPersistenceContext().isLoadFinished() ) {
|
||||
throw new SessionException( "proxies cannot be fetched by a stateless session" );
|
||||
}
|
||||
// unless we are still in the process of handling a top-level load
|
||||
return get( entityName, id );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -275,19 +280,54 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
boolean eager,
|
||||
boolean nullable) throws HibernateException {
|
||||
checkOpen();
|
||||
|
||||
EntityPersister persister = getFactory().getMetamodel().entityPersister( entityName );
|
||||
final EntityKey entityKey = generateEntityKey( id, persister );
|
||||
|
||||
// first, try to load it from the temp PC associated to this SS
|
||||
Object loaded = temporaryPersistenceContext.getEntity( generateEntityKey( id, persister ) );
|
||||
Object loaded = temporaryPersistenceContext.getEntity( entityKey );
|
||||
if ( loaded != null ) {
|
||||
// we found it in the temp PC. Should indicate we are in the midst of processing a result set
|
||||
// containing eager fetches via join fetch
|
||||
return loaded;
|
||||
}
|
||||
if ( !eager && persister.hasProxy() ) {
|
||||
// if the metadata allowed proxy creation and caller did not request forceful eager loading,
|
||||
// generate a proxy
|
||||
return persister.createProxy( id, this );
|
||||
|
||||
if ( !eager ) {
|
||||
// caller did not request forceful eager loading, see if we can create
|
||||
// some form of proxy
|
||||
|
||||
// first, check to see if we can use "bytecode proxies"
|
||||
|
||||
if ( allowBytecodeProxy
|
||||
&& persister.getEntityMetamodel().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
|
||||
|
||||
// we cannot use bytecode proxy for entities with subclasses
|
||||
if ( !persister.getEntityMetamodel().hasSubclasses() ) {
|
||||
final Object entity = persister.getEntityTuplizer().instantiate( id, this );
|
||||
|
||||
persister.getEntityMetamodel()
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.injectEnhancedEntityAsProxyInterceptor( entity, entityKey, this );
|
||||
|
||||
getPersistenceContext().addEntity( entityKey, entity );
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
// we could not use bytecode proxy, check to see if we can use HibernateProxy
|
||||
if ( persister.hasProxy() ) {
|
||||
final Object existingProxy = getPersistenceContext().getProxy( entityKey );
|
||||
if ( existingProxy != null ) {
|
||||
return getPersistenceContext().narrowProxy( existingProxy, persister, entityKey, null );
|
||||
}
|
||||
else {
|
||||
final Object proxy = persister.createProxy( id, this );
|
||||
getPersistenceContext().addProxy( entityKey, proxy );
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise immediately materialize it
|
||||
return get( entityName, id );
|
||||
}
|
||||
|
@ -424,6 +464,18 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
@Override
|
||||
public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
|
||||
checkOpen();
|
||||
|
||||
final Object result = getPersistenceContext().getEntity( key );
|
||||
if ( result != null ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
final Object newObject = getInterceptor().getEntity( key.getEntityName(), key.getIdentifier() );
|
||||
if ( newObject != null ) {
|
||||
getPersistenceContext().addEntity( key, newObject );
|
||||
return newObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,12 @@ public final class ArrayHelper {
|
|||
return -1;
|
||||
}
|
||||
|
||||
public static <T> T[] filledArray(T value, Class<T> valueJavaType, int size) {
|
||||
final T[] array = (T[]) Array.newInstance( valueJavaType, size );
|
||||
Arrays.fill( array, value );
|
||||
return array;
|
||||
}
|
||||
|
||||
public static String[] toStringArray(Object[] objects) {
|
||||
int length = objects.length;
|
||||
String[] result = new String[length];
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.ScrollMode;
|
|||
import org.hibernate.Session;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.WrongClassException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.cache.spi.FilterKey;
|
||||
import org.hibernate.cache.spi.QueryKey;
|
||||
import org.hibernate.cache.spi.QueryResultsCache;
|
||||
|
@ -50,6 +51,8 @@ import org.hibernate.engine.spi.EntityEntry;
|
|||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.EntityUniqueKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -66,6 +69,7 @@ import org.hibernate.internal.FetchingScrollableResultsImpl;
|
|||
import org.hibernate.internal.ScrollableResultsImpl;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.loader.entity.CascadeEntityLoader;
|
||||
import org.hibernate.loader.spi.AfterLoadAction;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -107,12 +111,14 @@ public abstract class Loader {
|
|||
private volatile ColumnNameCache columnNameCache;
|
||||
|
||||
private final boolean referenceCachingEnabled;
|
||||
private final boolean enhancementAsProxyEnabled;
|
||||
|
||||
private boolean isJdbc4 = true;
|
||||
|
||||
public Loader(SessionFactoryImplementor factory) {
|
||||
this.factory = factory;
|
||||
this.referenceCachingEnabled = factory.getSessionFactoryOptions().isDirectReferenceCacheEntriesEnabled();
|
||||
this.enhancementAsProxyEnabled = factory.getSessionFactoryOptions().isEnhancementAsProxyEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -829,6 +835,7 @@ public abstract class Loader {
|
|||
keys[targetIndex],
|
||||
object,
|
||||
lockModes[targetIndex],
|
||||
hydratedObjects,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
@ -1491,7 +1498,8 @@ public abstract class Loader {
|
|||
|
||||
Object version = session.getPersistenceContext().getEntry( entity ).getVersion();
|
||||
|
||||
if ( version != null ) { //null version means the object is in the process of being loaded somewhere else in the ResultSet
|
||||
if ( version != null ) {
|
||||
// null version means the object is in the process of being loaded somewhere else in the ResultSet
|
||||
final VersionType versionType = persister.getVersionType();
|
||||
final Object currentVersion = versionType.nullSafeGet(
|
||||
rs,
|
||||
|
@ -1526,7 +1534,7 @@ public abstract class Loader {
|
|||
final List hydratedObjects,
|
||||
final SharedSessionContractImplementor session) throws HibernateException, SQLException {
|
||||
final int cols = persisters.length;
|
||||
final EntityAliases[] descriptors = getEntityAliases();
|
||||
final EntityAliases[] entityAliases = getEntityAliases();
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf( "Result row: %s", StringHelper.toString( keys ) );
|
||||
|
@ -1546,7 +1554,6 @@ public abstract class Loader {
|
|||
//If the object is already loaded, return the loaded one
|
||||
object = session.getEntityUsingInterceptor( key );
|
||||
if ( object != null ) {
|
||||
//its already loaded so don't need to hydrate it
|
||||
instanceAlreadyLoaded(
|
||||
rs,
|
||||
i,
|
||||
|
@ -1554,6 +1561,7 @@ public abstract class Loader {
|
|||
key,
|
||||
object,
|
||||
lockModes[i],
|
||||
hydratedObjects,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
@ -1562,7 +1570,7 @@ public abstract class Loader {
|
|||
rs,
|
||||
i,
|
||||
persisters[i],
|
||||
descriptors[i].getRowIdAlias(),
|
||||
entityAliases[i].getRowIdAlias(),
|
||||
key,
|
||||
lockModes[i],
|
||||
optionalObjectKey,
|
||||
|
@ -1590,6 +1598,7 @@ public abstract class Loader {
|
|||
final EntityKey key,
|
||||
final Object object,
|
||||
final LockMode requestedLockMode,
|
||||
List hydratedObjects,
|
||||
final SharedSessionContractImplementor session)
|
||||
throws HibernateException, SQLException {
|
||||
if ( !persister.isInstance( object ) ) {
|
||||
|
@ -1600,7 +1609,42 @@ public abstract class Loader {
|
|||
);
|
||||
}
|
||||
|
||||
if ( LockMode.NONE != requestedLockMode && upgradeLocks() ) { //no point doing this if NONE was requested
|
||||
if ( persister.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() && enhancementAsProxyEnabled ) {
|
||||
if ( "merge".equals( session.getLoadQueryInfluencers().getInternalFetchProfile() ) ) {
|
||||
assert this instanceof CascadeEntityLoader;
|
||||
// we are processing a merge and have found an existing "managed copy" in the
|
||||
// session - we need to check if this copy is an enhanced-proxy and, if so,
|
||||
// perform the hydration just as if it were "not yet loaded"
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) object;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
hydrateEntityState(
|
||||
rs,
|
||||
i,
|
||||
persister,
|
||||
getEntityAliases()[i].getRowIdAlias(),
|
||||
key,
|
||||
hydratedObjects,
|
||||
session,
|
||||
getInstanceClass(
|
||||
rs,
|
||||
i,
|
||||
persister,
|
||||
key.getIdentifier(),
|
||||
session
|
||||
),
|
||||
object,
|
||||
requestedLockMode
|
||||
);
|
||||
|
||||
// EARLY EXIT!!!
|
||||
// - to skip the version check
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( LockMode.NONE != requestedLockMode && upgradeLocks() ) {
|
||||
final EntityEntry entry = session.getPersistenceContext().getEntry( object );
|
||||
if ( entry.getLockMode().lessThan( requestedLockMode ) ) {
|
||||
//we only check the version when _upgrading_ lock modes
|
||||
|
@ -1669,6 +1713,33 @@ public abstract class Loader {
|
|||
// (but don't yet initialize the object itself)
|
||||
// note that we acquire LockMode.READ even if it was not requested
|
||||
LockMode acquiredLockMode = lockMode == LockMode.NONE ? LockMode.READ : lockMode;
|
||||
hydrateEntityState(
|
||||
rs,
|
||||
i,
|
||||
persister,
|
||||
rowIdAlias,
|
||||
key,
|
||||
hydratedObjects,
|
||||
session,
|
||||
instanceClass,
|
||||
object,
|
||||
acquiredLockMode
|
||||
);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
private void hydrateEntityState(
|
||||
ResultSet rs,
|
||||
int i,
|
||||
Loadable persister,
|
||||
String rowIdAlias,
|
||||
EntityKey key,
|
||||
List hydratedObjects,
|
||||
SharedSessionContractImplementor session,
|
||||
String instanceClass,
|
||||
Object object,
|
||||
LockMode acquiredLockMode) throws SQLException {
|
||||
loadFromResultSet(
|
||||
rs,
|
||||
i,
|
||||
|
@ -1683,8 +1754,6 @@ public abstract class Loader {
|
|||
|
||||
//materialize associations (and initialize the object) later
|
||||
hydratedObjects.add( object );
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
private boolean isEagerPropertyFetchEnabled(int i) {
|
||||
|
|
|
@ -14,6 +14,9 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.WrongClassException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
|
||||
import org.hibernate.engine.internal.TwoPhaseLoad;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
|
@ -195,6 +198,31 @@ public class EntityReferenceInitializerImpl implements EntityReferenceInitialize
|
|||
// use the existing association as the hydrated state
|
||||
processingState.registerEntityInstance( existing );
|
||||
//context.registerHydratedEntity( entityReference, entityKey, existing );
|
||||
|
||||
// see if the entity is enhanced and is being used as a "proxy" (is fully uninitialized)
|
||||
final BytecodeEnhancementMetadata enhancementMetadata = entityReference.getEntityPersister()
|
||||
.getEntityMetamodel()
|
||||
.getBytecodeEnhancementMetadata();
|
||||
|
||||
if ( enhancementMetadata.isEnhancedForLazyLoading() ) {
|
||||
final BytecodeLazyAttributeInterceptor interceptor = enhancementMetadata.extractLazyInterceptor( existing );
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
final LockMode requestedLockMode = context.resolveLockMode( entityReference );
|
||||
final LockMode lockModeToAcquire = requestedLockMode == LockMode.NONE
|
||||
? LockMode.READ
|
||||
: requestedLockMode;
|
||||
|
||||
loadFromResultSet(
|
||||
resultSet,
|
||||
context,
|
||||
existing,
|
||||
getConcreteEntityTypeName( resultSet, context, entityKey ),
|
||||
entityKey,
|
||||
lockModeToAcquire
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.EntityMode;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.PropertyNotFoundException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
|
||||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
import org.hibernate.engine.spi.CascadeStyles;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
|
@ -233,29 +234,31 @@ public class Property implements Serializable, MetaAttributable {
|
|||
public void setLazy(boolean lazy) {
|
||||
this.lazy=lazy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this property lazy in the "bytecode" sense?
|
||||
*
|
||||
* Lazy here means whether we should push *something* to the entity
|
||||
* instance for this field in its "base fetch group". Mainly it affects
|
||||
* whether we should list this property's columns in the SQL select
|
||||
* for the owning entity when we load its "base fetch group".
|
||||
*
|
||||
* The "something" we push varies based on the nature (basic, etc) of
|
||||
* the property.
|
||||
*
|
||||
* @apiNote This form reports whether the property is considered part of the
|
||||
* base fetch group based solely on the mapping information. However,
|
||||
* {@link EnhancementHelper#includeInBaseFetchGroup} is used internally to make that
|
||||
* decision to account for {@link org.hibernate.cfg.AvailableSettings#ALLOW_ENHANCEMENT_AS_PROXY}
|
||||
*/
|
||||
public boolean isLazy() {
|
||||
if ( value instanceof ToOne ) {
|
||||
// both many-to-one and one-to-one are represented as a
|
||||
// Property. EntityPersister is relying on this value to
|
||||
// determine "lazy fetch groups" in terms of field-level
|
||||
// interception. So we need to make sure that we return
|
||||
// true here for the case of many-to-one and one-to-one
|
||||
// with lazy="no-proxy"
|
||||
//
|
||||
// * impl note - lazy="no-proxy" currently forces both
|
||||
// lazy and unwrap to be set to true. The other case we
|
||||
// are extremely interested in here is that of lazy="proxy"
|
||||
// where lazy is set to true, but unwrap is set to false.
|
||||
// thus we use both here under the assumption that this
|
||||
// return is really only ever used during persister
|
||||
// construction to determine the lazy property/field fetch
|
||||
// groupings. If that assertion changes then this check
|
||||
// needs to change as well. Partially, this is an issue with
|
||||
// the overloading of the term "lazy" here...
|
||||
ToOne toOneValue = ( ToOne ) value;
|
||||
return toOneValue.isLazy() && toOneValue.isUnwrapProxy();
|
||||
// For a many-to-one, this is always false. Whether the
|
||||
// association is EAGER, PROXY or NO-PROXY we want the fk
|
||||
// selected
|
||||
return false;
|
||||
}
|
||||
|
||||
return lazy;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ import org.hibernate.Session;
|
|||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.StaleStateException;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeDescriptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
|
||||
|
@ -74,6 +77,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.engine.spi.PersistenceContext.NaturalIdHelper;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.ValueInclusion;
|
||||
|
@ -551,7 +555,7 @@ public abstract class AbstractEntityPersister
|
|||
this.naturalIdRegionAccessStrategy = null;
|
||||
}
|
||||
|
||||
this.entityMetamodel = new EntityMetamodel( persistentClass, this, factory );
|
||||
this.entityMetamodel = new EntityMetamodel( persistentClass, this, creationContext );
|
||||
this.entityTuplizer = this.entityMetamodel.getTuplizer();
|
||||
|
||||
if ( entityMetamodel.isMutable() ) {
|
||||
|
@ -684,7 +688,20 @@ public abstract class AbstractEntityPersister
|
|||
propertyColumnWriters[i] = colWriters;
|
||||
propertyColumnAliases[i] = colAliases;
|
||||
|
||||
if ( lazyAvailable && prop.isLazy() ) {
|
||||
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
|
||||
prop,
|
||||
entityMetamodel.isInstrumented(),
|
||||
creationContext.getSessionFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled(),
|
||||
associatedEntityName -> {
|
||||
final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName );
|
||||
if ( bootEntityDescriptor == null ) {
|
||||
return false;
|
||||
}
|
||||
return bootEntityDescriptor.hasSubclasses();
|
||||
}
|
||||
);
|
||||
|
||||
if ( lazy ) {
|
||||
lazyNames.add( prop.getName() );
|
||||
lazyNumbers.add( i );
|
||||
lazyTypes.add( prop.getValue().getType() );
|
||||
|
@ -754,7 +771,18 @@ public abstract class AbstractEntityPersister
|
|||
int[] colnos = new int[prop.getColumnSpan()];
|
||||
int[] formnos = new int[prop.getColumnSpan()];
|
||||
int l = 0;
|
||||
Boolean lazy = Boolean.valueOf( prop.isLazy() && lazyAvailable );
|
||||
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
|
||||
prop,
|
||||
entityMetamodel.isInstrumented(),
|
||||
creationContext.getSessionFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled(),
|
||||
associatedEntityName -> {
|
||||
final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName );
|
||||
if ( bootEntityDescriptor == null ) {
|
||||
return false;
|
||||
}
|
||||
return bootEntityDescriptor.hasSubclasses();
|
||||
}
|
||||
);
|
||||
while ( colIter.hasNext() ) {
|
||||
Selectable thing = (Selectable) colIter.next();
|
||||
if ( thing.isFormula() ) {
|
||||
|
@ -1025,7 +1053,7 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
public Object initializeLazyProperty(String fieldName, Object entity, SharedSessionContractImplementor session) {
|
||||
final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
|
||||
final InterceptorImplementor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
|
||||
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
|
||||
assert interceptor != null : "Expecting bytecode interceptor to be non-null";
|
||||
|
||||
if ( hasCollections() ) {
|
||||
|
@ -1052,10 +1080,10 @@ public abstract class AbstractEntityPersister
|
|||
session.getPersistenceContext().addUninitializedCollection( persister, collection, key );
|
||||
}
|
||||
|
||||
// HHH-11161 Initialize, if the collection is not extra lazy
|
||||
if ( !persister.isExtraLazy() ) {
|
||||
session.initializeCollection( collection, false );
|
||||
}
|
||||
// // HHH-11161 Initialize, if the collection is not extra lazy
|
||||
// if ( !persister.isExtraLazy() ) {
|
||||
// session.initializeCollection( collection, false );
|
||||
// }
|
||||
interceptor.attributeInitialized( fieldName );
|
||||
|
||||
if ( collectionType.isArrayType() ) {
|
||||
|
@ -1146,10 +1174,10 @@ public abstract class AbstractEntityPersister
|
|||
throw new AssertionFailure( "no lazy properties" );
|
||||
}
|
||||
|
||||
final InterceptorImplementor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
|
||||
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
|
||||
assert interceptor != null : "Expecting bytecode interceptor to be non-null";
|
||||
|
||||
LOG.trace( "Initializing lazy properties from datastore" );
|
||||
LOG.tracef( "Initializing lazy properties from datastore (triggered for `%s`)", fieldName );
|
||||
|
||||
final String fetchGroup = getEntityMetamodel().getBytecodeEnhancementMetadata()
|
||||
.getLazyAttributesMetadata()
|
||||
|
@ -4273,6 +4301,50 @@ public abstract class AbstractEntityPersister
|
|||
return loader.load( id, optionalObject, session, lockOptions );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object initializeEnhancedEntityUsedAsProxy(
|
||||
Object entity,
|
||||
String nameOfAttributeBeingAccessed,
|
||||
SharedSessionContractImplementor session) {
|
||||
final BytecodeEnhancementMetadata enhancementMetadata = getEntityMetamodel().getBytecodeEnhancementMetadata();
|
||||
final BytecodeLazyAttributeInterceptor currentInterceptor = enhancementMetadata.extractLazyInterceptor( entity );
|
||||
if ( currentInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
final EnhancementAsProxyLazinessInterceptor proxyInterceptor = (EnhancementAsProxyLazinessInterceptor) currentInterceptor;
|
||||
|
||||
readLockLoader.load(
|
||||
proxyInterceptor.getEntityKey().getIdentifier(),
|
||||
entity,
|
||||
session,
|
||||
LockOptions.READ
|
||||
);
|
||||
|
||||
final LazyAttributeLoadingInterceptor interceptor = enhancementMetadata.injectInterceptor(
|
||||
entity,
|
||||
proxyInterceptor.getEntityKey().getIdentifier(),
|
||||
session
|
||||
);
|
||||
|
||||
final Object value;
|
||||
if ( nameOfAttributeBeingAccessed == null ) {
|
||||
return null;
|
||||
}
|
||||
else if ( interceptor.isAttributeLoaded( nameOfAttributeBeingAccessed ) ) {
|
||||
value = getEntityTuplizer().getPropertyValue( entity, nameOfAttributeBeingAccessed );
|
||||
}
|
||||
else {
|
||||
value = ( (LazyPropertyInitializer) this ).initializeLazyProperty( nameOfAttributeBeingAccessed, entity, session );
|
||||
}
|
||||
|
||||
return interceptor.readObject(
|
||||
entity,
|
||||
nameOfAttributeBeingAccessed,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
throw new IllegalStateException( );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List multiLoad(Serializable[] ids, SharedSessionContractImplementor session, MultiLoadOptions loadOptions) {
|
||||
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad(
|
||||
|
@ -4582,9 +4654,14 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
public void afterReassociate(Object entity, SharedSessionContractImplementor session) {
|
||||
if ( getEntityMetamodel().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
|
||||
LazyAttributeLoadingInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata().extractInterceptor( entity );
|
||||
final BytecodeLazyAttributeInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata()
|
||||
.extractLazyInterceptor( entity );
|
||||
if ( interceptor == null ) {
|
||||
getEntityMetamodel().getBytecodeEnhancementMetadata().injectInterceptor( entity, session );
|
||||
getEntityMetamodel().getBytecodeEnhancementMetadata().injectInterceptor(
|
||||
entity,
|
||||
getIdentifier( entity, session ),
|
||||
session
|
||||
);
|
||||
}
|
||||
else {
|
||||
interceptor.setSession( session );
|
||||
|
@ -5453,6 +5530,11 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
@Override
|
||||
public BytecodeEnhancementMetadata getInstrumentationMetadata() {
|
||||
return getBytecodeEnhancementMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BytecodeEnhancementMetadata getBytecodeEnhancementMetadata() {
|
||||
return entityMetamodel.getBytecodeEnhancementMetadata();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
|
||||
import org.hibernate.bytecode.spi.NotInstrumentedException;
|
||||
import org.hibernate.cache.spi.access.EntityDataAccess;
|
||||
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
|
||||
import org.hibernate.cache.spi.entry.CacheEntry;
|
||||
|
@ -132,6 +134,20 @@ public interface EntityPersister extends EntityDefinition {
|
|||
*/
|
||||
EntityMetamodel getEntityMetamodel();
|
||||
|
||||
/**
|
||||
* Called from {@link EnhancementAsProxyLazinessInterceptor} to trigger load of
|
||||
* the entity's non-lazy state as well as the named attribute we are accessing
|
||||
* if it is still uninitialized after fetching non-lazy state
|
||||
*/
|
||||
default Object initializeEnhancedEntityUsedAsProxy(
|
||||
Object entity,
|
||||
String nameOfAttributeBeingAccessed,
|
||||
SharedSessionContractImplementor session) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Initialization of entity enhancement used to act like a proxy is not supported by this EntityPersister : " + getClass().getName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given name represents a subclass entity
|
||||
* (or this entity itself) of the entity mapped by this persister.
|
||||
|
@ -798,7 +814,11 @@ public interface EntityPersister extends EntityDefinition {
|
|||
EntityTuplizer getEntityTuplizer();
|
||||
|
||||
BytecodeEnhancementMetadata getInstrumentationMetadata();
|
||||
|
||||
|
||||
default BytecodeEnhancementMetadata getBytecodeEnhancementMetadata() {
|
||||
return getInstrumentationMetadata();
|
||||
}
|
||||
|
||||
FilterAliasGenerator getFilterAliasGenerator(final String rootAlias);
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
package org.hibernate.tuple;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
|
||||
import org.hibernate.engine.internal.UnsavedValueFactory;
|
||||
import org.hibernate.engine.spi.IdentifierValue;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -153,7 +155,8 @@ public final class PropertyFactory {
|
|||
SessionFactoryImplementor sessionFactory,
|
||||
int attributeNumber,
|
||||
Property property,
|
||||
boolean lazyAvailable) {
|
||||
boolean lazyAvailable,
|
||||
Function<String,Boolean> hasSubclassChecker) {
|
||||
final Type type = property.getValue().getType();
|
||||
|
||||
final NonIdentifierAttributeNature nature = decode( type );
|
||||
|
@ -168,6 +171,13 @@ public final class PropertyFactory {
|
|||
boolean alwaysDirtyCheck = type.isAssociationType() &&
|
||||
( (AssociationType) type ).isAlwaysDirtyChecked();
|
||||
|
||||
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
|
||||
property,
|
||||
lazyAvailable,
|
||||
sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled(),
|
||||
hasSubclassChecker
|
||||
);
|
||||
|
||||
switch ( nature ) {
|
||||
case BASIC: {
|
||||
return new EntityBasedBasicAttribute(
|
||||
|
@ -177,7 +187,7 @@ public final class PropertyFactory {
|
|||
property.getName(),
|
||||
type,
|
||||
new BaselineAttributeInformation.Builder()
|
||||
.setLazy( lazyAvailable && property.isLazy() )
|
||||
.setLazy( lazy )
|
||||
.setInsertable( property.isInsertable() )
|
||||
.setUpdateable( property.isUpdateable() )
|
||||
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
|
||||
|
@ -197,7 +207,7 @@ public final class PropertyFactory {
|
|||
property.getName(),
|
||||
(CompositeType) type,
|
||||
new BaselineAttributeInformation.Builder()
|
||||
.setLazy( lazyAvailable && property.isLazy() )
|
||||
.setLazy( lazy )
|
||||
.setInsertable( property.isInsertable() )
|
||||
.setUpdateable( property.isUpdateable() )
|
||||
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
|
||||
|
@ -219,7 +229,7 @@ public final class PropertyFactory {
|
|||
property.getName(),
|
||||
(AssociationType) type,
|
||||
new BaselineAttributeInformation.Builder()
|
||||
.setLazy( lazyAvailable && property.isLazy() )
|
||||
.setLazy( lazy )
|
||||
.setInsertable( property.isInsertable() )
|
||||
.setUpdateable( property.isUpdateable() )
|
||||
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
|
||||
|
@ -279,7 +289,9 @@ public final class PropertyFactory {
|
|||
return new StandardProperty(
|
||||
property.getName(),
|
||||
type,
|
||||
lazyAvailable && property.isLazy(),
|
||||
// only called for embeddable sub-attributes which are never (yet) lazy
|
||||
//lazyAvailable && property.isLazy(),
|
||||
false,
|
||||
property.isInsertable(),
|
||||
property.isUpdateable(),
|
||||
property.getValueGenerationStrategy(),
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.EntityNameResolver;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
|
||||
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
|
@ -152,7 +153,8 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
|
|||
|
||||
instantiator = buildInstantiator( entityMetamodel, mappingInfo );
|
||||
|
||||
if ( entityMetamodel.isLazy() && !entityMetamodel.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
|
||||
// if ( entityMetamodel.isLazy() && !entityMetamodel.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
|
||||
if ( entityMetamodel.isLazy() ) {
|
||||
proxyFactory = buildProxyFactory( mappingInfo, idGetter, idSetter );
|
||||
if ( proxyFactory == null ) {
|
||||
entityMetamodel.setLazy( false );
|
||||
|
@ -535,18 +537,24 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
|
|||
@Override
|
||||
public Object[] getPropertyValues(Object entity) {
|
||||
final BytecodeEnhancementMetadata enhancementMetadata = entityMetamodel.getBytecodeEnhancementMetadata();
|
||||
final LazyAttributesMetadata lazyAttributesMetadata = enhancementMetadata.getLazyAttributesMetadata();
|
||||
|
||||
final int span = entityMetamodel.getPropertySpan();
|
||||
final Object[] result = new Object[span];
|
||||
|
||||
for ( int j = 0; j < span; j++ ) {
|
||||
NonIdentifierAttribute property = entityMetamodel.getProperties()[j];
|
||||
if ( !property.isLazy() || enhancementMetadata.isAttributeLoaded( entity, property.getName() ) ) {
|
||||
// if the attribute is not lazy (bytecode sense), we can just use the value from the instance
|
||||
// if the attribute is lazy but has been initialized we can just use the value from the instance
|
||||
// todo : there should be a third case here when we merge transient instances
|
||||
if ( ! lazyAttributesMetadata.isLazyAttribute( entityMetamodel.getPropertyNames()[j] )
|
||||
|| enhancementMetadata.isAttributeLoaded( entity, entityMetamodel.getPropertyNames()[j] ) ) {
|
||||
result[j] = getters[j].get( entity );
|
||||
}
|
||||
else {
|
||||
result[j] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -718,7 +726,8 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
|
|||
return instantiator;
|
||||
}
|
||||
|
||||
protected final ProxyFactory getProxyFactory() {
|
||||
@Override
|
||||
public final ProxyFactory getProxyFactory() {
|
||||
return proxyFactory;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.tuple.entity;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
|
||||
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
|
||||
import org.hibernate.bytecode.spi.NotInstrumentedException;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
|
@ -44,15 +47,37 @@ public class BytecodeEnhancementMetadataNonPojoImpl implements BytecodeEnhanceme
|
|||
@Override
|
||||
public LazyAttributeLoadingInterceptor injectInterceptor(
|
||||
Object entity,
|
||||
Object identifier,
|
||||
SharedSessionContractImplementor session) throws NotInstrumentedException {
|
||||
throw new NotInstrumentedException( errorMsg );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectInterceptor(
|
||||
Object entity,
|
||||
PersistentAttributeInterceptor interceptor,
|
||||
SharedSessionContractImplementor session) {
|
||||
throw new NotInstrumentedException( errorMsg );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectEnhancedEntityAsProxyInterceptor(
|
||||
Object entity,
|
||||
EntityKey entityKey,
|
||||
SharedSessionContractImplementor session) {
|
||||
throw new NotInstrumentedException( errorMsg );
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyAttributeLoadingInterceptor extractInterceptor(Object entity) throws NotInstrumentedException {
|
||||
throw new NotInstrumentedException( errorMsg );
|
||||
}
|
||||
|
||||
@Override
|
||||
public BytecodeLazyAttributeInterceptor extractLazyInterceptor(Object entity) throws NotInstrumentedException {
|
||||
throw new NotInstrumentedException( errorMsg );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasUnFetchedAttributes(Object entity) {
|
||||
return false;
|
||||
|
|
|
@ -6,29 +6,46 @@
|
|||
*/
|
||||
package org.hibernate.tuple.entity;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
|
||||
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
|
||||
import org.hibernate.bytecode.spi.NotInstrumentedException;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.type.CompositeType;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhancementMetadata {
|
||||
public static BytecodeEnhancementMetadata from(PersistentClass persistentClass) {
|
||||
/**
|
||||
* Static constructor
|
||||
*/
|
||||
public static BytecodeEnhancementMetadata from(
|
||||
PersistentClass persistentClass,
|
||||
Set<String> identifierAttributeNames,
|
||||
CompositeType nonAggregatedCidMapper,
|
||||
boolean allowEnhancementAsProxy,
|
||||
Function<String,Boolean> hasSubclassChecker) {
|
||||
final Class mappedClass = persistentClass.getMappedClass();
|
||||
final boolean enhancedForLazyLoading = PersistentAttributeInterceptable.class.isAssignableFrom( mappedClass );
|
||||
final LazyAttributesMetadata lazyAttributesMetadata = enhancedForLazyLoading
|
||||
? LazyAttributesMetadata.from( persistentClass )
|
||||
? LazyAttributesMetadata.from( persistentClass, true, allowEnhancementAsProxy, hasSubclassChecker )
|
||||
: LazyAttributesMetadata.nonEnhanced( persistentClass.getEntityName() );
|
||||
|
||||
return new BytecodeEnhancementMetadataPojoImpl(
|
||||
persistentClass.getEntityName(),
|
||||
mappedClass,
|
||||
identifierAttributeNames,
|
||||
nonAggregatedCidMapper,
|
||||
enhancedForLazyLoading,
|
||||
lazyAttributesMetadata
|
||||
);
|
||||
|
@ -36,16 +53,26 @@ public class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhancementM
|
|||
|
||||
private final String entityName;
|
||||
private final Class entityClass;
|
||||
private final Set<String> identifierAttributeNames;
|
||||
private final CompositeType nonAggregatedCidMapper;
|
||||
private final boolean enhancedForLazyLoading;
|
||||
private final LazyAttributesMetadata lazyAttributesMetadata;
|
||||
|
||||
public BytecodeEnhancementMetadataPojoImpl(
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected BytecodeEnhancementMetadataPojoImpl(
|
||||
String entityName,
|
||||
Class entityClass,
|
||||
Set<String> identifierAttributeNames,
|
||||
CompositeType nonAggregatedCidMapper,
|
||||
boolean enhancedForLazyLoading,
|
||||
LazyAttributesMetadata lazyAttributesMetadata) {
|
||||
this.nonAggregatedCidMapper = nonAggregatedCidMapper;
|
||||
assert identifierAttributeNames != null;
|
||||
assert !identifierAttributeNames.isEmpty();
|
||||
|
||||
this.entityName = entityName;
|
||||
this.entityClass = entityClass;
|
||||
this.identifierAttributeNames = identifierAttributeNames;
|
||||
this.enhancedForLazyLoading = enhancedForLazyLoading;
|
||||
this.lazyAttributesMetadata = lazyAttributesMetadata;
|
||||
}
|
||||
|
@ -67,18 +94,114 @@ public class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhancementM
|
|||
|
||||
@Override
|
||||
public boolean hasUnFetchedAttributes(Object entity) {
|
||||
LazyAttributeLoadingInterceptor interceptor = enhancedForLazyLoading ? extractInterceptor( entity ) : null;
|
||||
return interceptor != null && interceptor.hasAnyUninitializedAttributes();
|
||||
if ( ! enhancedForLazyLoading ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final BytecodeLazyAttributeInterceptor interceptor = extractLazyInterceptor( entity );
|
||||
if ( interceptor instanceof LazyAttributeLoadingInterceptor ) {
|
||||
return ( (LazyAttributeLoadingInterceptor) interceptor ).hasAnyUninitializedAttributes();
|
||||
}
|
||||
|
||||
//noinspection RedundantIfStatement
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttributeLoaded(Object entity, String attributeName) {
|
||||
LazyAttributeLoadingInterceptor interceptor = enhancedForLazyLoading ? extractInterceptor( entity ) : null;
|
||||
return interceptor == null || interceptor.isAttributeLoaded( attributeName );
|
||||
if ( ! enhancedForLazyLoading ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final BytecodeLazyAttributeInterceptor interceptor = extractLazyInterceptor( entity );
|
||||
if ( interceptor instanceof LazyAttributeLoadingInterceptor ) {
|
||||
return ( (LazyAttributeLoadingInterceptor) interceptor ).isAttributeLoaded( attributeName );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyAttributeLoadingInterceptor extractInterceptor(Object entity) throws NotInstrumentedException {
|
||||
return (LazyAttributeLoadingInterceptor) extractLazyInterceptor( entity );
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyAttributeLoadingInterceptor injectInterceptor(
|
||||
Object entity,
|
||||
Object identifier,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( !enhancedForLazyLoading ) {
|
||||
throw new NotInstrumentedException( "Entity class [" + entityClass.getName() + "] is not enhanced for lazy loading" );
|
||||
}
|
||||
|
||||
if ( !entityClass.isInstance( entity ) ) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Passed entity instance [%s] is not of expected type [%s]",
|
||||
entity,
|
||||
getEntityName()
|
||||
)
|
||||
);
|
||||
}
|
||||
final LazyAttributeLoadingInterceptor interceptor = new LazyAttributeLoadingInterceptor(
|
||||
getEntityName(),
|
||||
identifier,
|
||||
lazyAttributesMetadata.getLazyAttributeNames(),
|
||||
session
|
||||
);
|
||||
|
||||
injectInterceptor( entity, interceptor, session );
|
||||
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectEnhancedEntityAsProxyInterceptor(
|
||||
Object entity,
|
||||
EntityKey entityKey,
|
||||
SharedSessionContractImplementor session) {
|
||||
injectInterceptor(
|
||||
entity,
|
||||
new EnhancementAsProxyLazinessInterceptor(
|
||||
entityName,
|
||||
identifierAttributeNames,
|
||||
nonAggregatedCidMapper,
|
||||
entityKey,
|
||||
session
|
||||
),
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectInterceptor(
|
||||
Object entity,
|
||||
PersistentAttributeInterceptor interceptor,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( !enhancedForLazyLoading ) {
|
||||
throw new NotInstrumentedException( "Entity class [" + entityClass.getName() + "] is not enhanced for lazy loading" );
|
||||
}
|
||||
|
||||
if ( !entityClass.isInstance( entity ) ) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Passed entity instance [%s] is not of expected type [%s]",
|
||||
entity,
|
||||
getEntityName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
( (PersistentAttributeInterceptable) entity ).$$_hibernate_setInterceptor( interceptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public BytecodeLazyAttributeInterceptor extractLazyInterceptor(Object entity) throws NotInstrumentedException {
|
||||
if ( !enhancedForLazyLoading ) {
|
||||
throw new NotInstrumentedException( "Entity class [" + entityClass.getName() + "] is not enhanced for lazy loading" );
|
||||
}
|
||||
|
@ -98,31 +221,7 @@ public class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhancementM
|
|||
return null;
|
||||
}
|
||||
|
||||
return (LazyAttributeLoadingInterceptor) interceptor;
|
||||
return (BytecodeLazyAttributeInterceptor) interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyAttributeLoadingInterceptor injectInterceptor(Object entity, SharedSessionContractImplementor session) {
|
||||
if ( !enhancedForLazyLoading ) {
|
||||
throw new NotInstrumentedException( "Entity class [" + entityClass.getName() + "] is not enhanced for lazy loading" );
|
||||
}
|
||||
|
||||
if ( !entityClass.isInstance( entity ) ) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Passed entity instance [%s] is not of expected type [%s]",
|
||||
entity,
|
||||
getEntityName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final LazyAttributeLoadingInterceptor interceptor = new LazyAttributeLoadingInterceptor(
|
||||
getEntityName(),
|
||||
lazyAttributesMetadata.getLazyAttributeNames(),
|
||||
session
|
||||
);
|
||||
( (PersistentAttributeInterceptable) entity ).$$_hibernate_setInterceptor( interceptor );
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.tuple.entity;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -18,6 +19,7 @@ import java.util.Set;
|
|||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
|
||||
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
|
||||
import org.hibernate.cfg.NotYetImplementedException;
|
||||
import org.hibernate.engine.OptimisticLockStyle;
|
||||
|
@ -31,6 +33,7 @@ import org.hibernate.mapping.Component;
|
|||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||
import org.hibernate.tuple.GenerationTiming;
|
||||
import org.hibernate.tuple.IdentifierProperty;
|
||||
import org.hibernate.tuple.InDatabaseValueGenerationStrategy;
|
||||
|
@ -125,8 +128,8 @@ public class EntityMetamodel implements Serializable {
|
|||
public EntityMetamodel(
|
||||
PersistentClass persistentClass,
|
||||
EntityPersister persister,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
final PersisterCreationContext creationContext) {
|
||||
this.sessionFactory = creationContext.getSessionFactory();
|
||||
|
||||
name = persistentClass.getEntityName();
|
||||
rootName = persistentClass.getRootClass().getEntityName();
|
||||
|
@ -139,7 +142,37 @@ public class EntityMetamodel implements Serializable {
|
|||
versioned = persistentClass.isVersioned();
|
||||
|
||||
if ( persistentClass.hasPojoRepresentation() ) {
|
||||
bytecodeEnhancementMetadata = BytecodeEnhancementMetadataPojoImpl.from( persistentClass );
|
||||
final Component identifierMapperComponent = persistentClass.getIdentifierMapper();
|
||||
final CompositeType nonAggregatedCidMapper;
|
||||
final Set<String> idAttributeNames;
|
||||
|
||||
if ( identifierMapperComponent != null ) {
|
||||
nonAggregatedCidMapper = (CompositeType) identifierMapperComponent.getType();
|
||||
idAttributeNames = new HashSet<>( );
|
||||
//noinspection unchecked
|
||||
final Iterator<String> propertyItr = identifierMapperComponent.getPropertyIterator();
|
||||
while ( propertyItr.hasNext() ) {
|
||||
idAttributeNames.add( propertyItr.next() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
nonAggregatedCidMapper = null;
|
||||
idAttributeNames = Collections.singleton( identifierAttribute.getName() );
|
||||
}
|
||||
|
||||
bytecodeEnhancementMetadata = BytecodeEnhancementMetadataPojoImpl.from(
|
||||
persistentClass,
|
||||
idAttributeNames,
|
||||
nonAggregatedCidMapper,
|
||||
sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled(),
|
||||
associatedEntityName -> {
|
||||
final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName );
|
||||
if ( bootEntityDescriptor == null ) {
|
||||
return false;
|
||||
}
|
||||
return bootEntityDescriptor.hasSubclasses();
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
bytecodeEnhancementMetadata = new BytecodeEnhancementMetadataNonPojoImpl( persistentClass.getEntityName() );
|
||||
|
@ -201,7 +234,14 @@ public class EntityMetamodel implements Serializable {
|
|||
sessionFactory,
|
||||
i,
|
||||
prop,
|
||||
bytecodeEnhancementMetadata.isEnhancedForLazyLoading()
|
||||
bytecodeEnhancementMetadata.isEnhancedForLazyLoading(),
|
||||
associatedEntityName -> {
|
||||
final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName );
|
||||
if ( bootEntityDescriptor == null ) {
|
||||
return false;
|
||||
}
|
||||
return bootEntityDescriptor.hasSubclasses();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -217,10 +257,23 @@ public class EntityMetamodel implements Serializable {
|
|||
}
|
||||
|
||||
// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
boolean lazy = prop.isLazy() && bytecodeEnhancementMetadata.isEnhancedForLazyLoading();
|
||||
boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
|
||||
prop,
|
||||
bytecodeEnhancementMetadata.isEnhancedForLazyLoading(),
|
||||
sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled(),
|
||||
associatedEntityName -> {
|
||||
final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName );
|
||||
if ( bootEntityDescriptor == null ) {
|
||||
return false;
|
||||
}
|
||||
return bootEntityDescriptor.hasSubclasses();
|
||||
}
|
||||
);
|
||||
|
||||
if ( lazy ) {
|
||||
hasLazy = true;
|
||||
}
|
||||
|
||||
propertyLaziness[i] = lazy;
|
||||
|
||||
propertyNames[i] = properties[i].getName();
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.tuple.Tuplizer;
|
||||
|
||||
/**
|
||||
|
@ -273,4 +274,8 @@ public interface EntityTuplizer extends Tuplizer {
|
|||
* @return The getter for the version property.
|
||||
*/
|
||||
Getter getVersionGetter();
|
||||
|
||||
default ProxyFactory getProxyFactory() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public class PojoEntityInstantiator extends PojoInstantiator {
|
|||
|
||||
PersistentAttributeInterceptor interceptor = new LazyAttributeLoadingInterceptor(
|
||||
entityMetamodel.getName(),
|
||||
null,
|
||||
entityMetamodel.getBytecodeEnhancementMetadata()
|
||||
.getLazyAttributesMetadata()
|
||||
.getLazyAttributeNames(),
|
||||
|
|
|
@ -16,7 +16,8 @@ import org.hibernate.EntityMode;
|
|||
import org.hibernate.EntityNameResolver;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.classic.Lifecycle;
|
||||
|
@ -268,22 +269,14 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer {
|
|||
|
||||
@Override
|
||||
public void afterInitialize(Object entity, SharedSessionContractImplementor session) {
|
||||
|
||||
// moving to multiple fetch groups, the idea of `lazyPropertiesAreUnfetched` really
|
||||
// needs to become either:
|
||||
// 1) the names of all un-fetched fetch groups
|
||||
// 2) the names of all fetched fetch groups
|
||||
// probably (2) is best
|
||||
//
|
||||
// ultimately this comes from EntityEntry, although usage-search seems to show it is never updated there.
|
||||
//
|
||||
// also org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyPropertiesFromDatastore()
|
||||
// needs to be re-worked
|
||||
|
||||
if ( entity instanceof PersistentAttributeInterceptable ) {
|
||||
final LazyAttributeLoadingInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata().extractInterceptor( entity );
|
||||
if ( interceptor == null ) {
|
||||
getEntityMetamodel().getBytecodeEnhancementMetadata().injectInterceptor( entity, session );
|
||||
final BytecodeLazyAttributeInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata().extractLazyInterceptor( entity );
|
||||
if ( interceptor == null || interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
getEntityMetamodel().getBytecodeEnhancementMetadata().injectInterceptor(
|
||||
entity,
|
||||
getIdentifier( entity, session ),
|
||||
session
|
||||
);
|
||||
}
|
||||
else {
|
||||
if ( interceptor.getLinkedSession() == null ) {
|
||||
|
|
|
@ -683,7 +683,8 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
getAssociatedEntityName(),
|
||||
id,
|
||||
eager,
|
||||
isNullable()
|
||||
isNullable(),
|
||||
unwrapProxy
|
||||
);
|
||||
|
||||
if ( proxyOrEntity instanceof HibernateProxy ) {
|
||||
|
|
|
@ -51,7 +51,7 @@ public class TestLazyPropertyOnPreUpdate extends BaseEntityManagerFunctionalTest
|
|||
@Before
|
||||
public void prepare() throws Exception {
|
||||
EntityPersister ep = entityManagerFactory().getMetamodel().entityPersister( EntityWithLazyProperty.class.getName() );
|
||||
assertTrue( ep.getInstrumentationMetadata().isEnhancedForLazyLoading() );
|
||||
assertTrue( ep.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() );
|
||||
|
||||
byte[] testArray = new byte[]{0x2A};
|
||||
|
||||
|
|
|
@ -209,15 +209,6 @@ public class BasicEnhancementTest {
|
|||
public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
|
||||
return WRITE_MARKER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getInitializedLazyAttributeNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attributeInitialized(String name) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- //
|
||||
|
|
|
@ -79,8 +79,10 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
|
|||
doInHibernate(
|
||||
this::sessionFactory, session -> {
|
||||
Employer employer = session.get( Employer.class, "RedHat" );
|
||||
|
||||
// Delete the associated entity first
|
||||
session.remove( employer );
|
||||
|
||||
for ( Employee employee : employer.getEmployees() ) {
|
||||
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
|
||||
session.remove( employee );
|
||||
|
@ -188,6 +190,7 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
|
|||
doInHibernate(
|
||||
this::sessionFactory, session -> {
|
||||
Employee employee = session.get( Employee.class, "Jack" );
|
||||
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
|
||||
|
||||
// Get and delete an Employer that is not associated with employee
|
||||
Employer employer = session.get( Employer.class, "RedHat" );
|
||||
|
@ -196,15 +199,48 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
|
|||
// employee.employer is uninitialized. Since the column for employee.employer
|
||||
// is a foreign key, and there is an Employer that has already been removed,
|
||||
// employee.employer will need to be iniitialized to determine if
|
||||
// employee.employee is nullifiable.
|
||||
// employee.employer is nullifiable.
|
||||
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
|
||||
session.remove( employee );
|
||||
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @implSpec Same as {@link #testRemoveEntityWithNullLazyManyToOne} but
|
||||
* deleting the Employer linked to the loaded Employee
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveEntityWithLinkedLazyManyToOne() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Employer employer = new Employer( "RedHat" );
|
||||
session.persist( employer );
|
||||
Employee employee = new Employee( "Jack" );
|
||||
employee.setEmployer( employer );
|
||||
session.persist( employee );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Employee employee = session.get( Employee.class, "Jack" );
|
||||
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
|
||||
|
||||
// Get and delete an Employer that is not associated with employee
|
||||
Employer employer = session.get( Employer.class, "RedHat" );
|
||||
session.remove( employer );
|
||||
|
||||
// employee.employer is uninitialized. Since the column for employee.employer
|
||||
// is a foreign key, and there is an Employer that has already been removed,
|
||||
// employee.employer will need to be iniitialized to determine if
|
||||
// employee.employer is nullifiable.
|
||||
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
|
||||
session.remove( employee );
|
||||
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void checkEntityEntryState(
|
||||
|
|
|
@ -0,0 +1,476 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.StatelessSession;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
public class StatelessQueryScrollingTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Test
|
||||
public void testDynamicFetchScroll() {
|
||||
final StatelessSession statelessSession = sessionFactory().openStatelessSession();
|
||||
|
||||
final Query query = statelessSession.createQuery( "from Task t join fetch t.resource join fetch t.user");
|
||||
final ScrollableResults scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY);
|
||||
while ( scrollableResults.next() ) {
|
||||
Task taskRef = (Task) scrollableResults.get( 0 );
|
||||
assertTrue( Hibernate.isInitialized( taskRef ) );
|
||||
assertTrue( Hibernate.isInitialized( taskRef.getUser() ) );
|
||||
assertTrue( Hibernate.isInitialized( taskRef.getResource() ) );
|
||||
assertFalse( Hibernate.isInitialized( taskRef.getResource().getOwner() ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicFetchCollectionScroll() {
|
||||
StatelessSession statelessSession = sessionFactory().openStatelessSession();
|
||||
statelessSession.beginTransaction();
|
||||
|
||||
final Query query = statelessSession.createQuery( "select p from Producer p join fetch p.products" );
|
||||
final ScrollableResults scrollableResults = query.scroll(ScrollMode.FORWARD_ONLY);
|
||||
while ( scrollableResults.next() ) {
|
||||
Producer producer = (Producer) scrollableResults.get( 0 );
|
||||
assertTrue( Hibernate.isInitialized( producer ) );
|
||||
assertTrue( Hibernate.isPropertyInitialized( producer, "products" ) );
|
||||
assertTrue( Hibernate.isInitialized( producer.getProducts() ) );
|
||||
|
||||
for (Product product : producer.getProducts()) {
|
||||
assertTrue( Hibernate.isInitialized( product ) );
|
||||
assertFalse( Hibernate.isInitialized( product.getVendor() ) );
|
||||
}
|
||||
}
|
||||
|
||||
statelessSession.getTransaction().commit();
|
||||
statelessSession.close();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void createTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Date now = new Date();
|
||||
User me = new User( "me" );
|
||||
User you = new User( "you" );
|
||||
Resource yourClock = new Resource( "clock", you );
|
||||
Task task = new Task( me, "clean", yourClock, now ); // :)
|
||||
|
||||
session.save( me );
|
||||
session.save( you );
|
||||
session.save( yourClock );
|
||||
session.save( task );
|
||||
|
||||
User u3 = new User( "U3" );
|
||||
User u4 = new User( "U4" );
|
||||
Resource it = new Resource( "it", u4 );
|
||||
Task task2 = new Task( u3, "beat", it, now ); // :))
|
||||
|
||||
session.save( u3 );
|
||||
session.save( u4 );
|
||||
session.save( it );
|
||||
session.save( task2 );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Producer p1 = new Producer( 1, "Acme" );
|
||||
Producer p2 = new Producer( 2, "ABC" );
|
||||
|
||||
session.save( p1 );
|
||||
session.save( p2 );
|
||||
|
||||
Vendor v1 = new Vendor( 1, "v1" );
|
||||
Vendor v2 = new Vendor( 2, "v2" );
|
||||
|
||||
session.save( v1 );
|
||||
session.save( v2 );
|
||||
|
||||
final Product product1 = new Product(1, "123", v1, p1);
|
||||
final Product product2 = new Product(2, "456", v1, p1);
|
||||
final Product product3 = new Product(3, "789", v1, p2);
|
||||
|
||||
session.save( product1 );
|
||||
session.save( product2 );
|
||||
session.save( product3 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void deleteTestData() {
|
||||
inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "delete Task" ).executeUpdate();
|
||||
s.createQuery( "delete Resource" ).executeUpdate();
|
||||
s.createQuery( "delete User" ).executeUpdate();
|
||||
|
||||
s.createQuery( "delete Product" ).executeUpdate();
|
||||
s.createQuery( "delete Producer" ).executeUpdate();
|
||||
s.createQuery( "delete Vendor" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( Task.class );
|
||||
sources.addAnnotatedClass( User.class );
|
||||
sources.addAnnotatedClass( Resource.class );
|
||||
sources.addAnnotatedClass( Product.class );
|
||||
sources.addAnnotatedClass( Producer.class );
|
||||
sources.addAnnotatedClass( Vendor.class );
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Collection fetch scrolling
|
||||
|
||||
@Entity( name = "Producer" )
|
||||
public static class Producer {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany( mappedBy = "producer", fetch = FetchType.LAZY )
|
||||
private Set<Product> products = new HashSet<>();
|
||||
|
||||
public Producer() {
|
||||
}
|
||||
|
||||
public Producer(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Product> getProducts() {
|
||||
return products;
|
||||
}
|
||||
|
||||
public void setProducts(Set<Product> products) {
|
||||
this.products = products;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Product" )
|
||||
public static class Product {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String sku;
|
||||
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
private Vendor vendor;
|
||||
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
private Producer producer;
|
||||
|
||||
public Product() {
|
||||
}
|
||||
|
||||
public Product(Integer id, String sku, Vendor vendor, Producer producer) {
|
||||
this.id = id;
|
||||
this.sku = sku;
|
||||
this.vendor = vendor;
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSku() {
|
||||
return sku;
|
||||
}
|
||||
|
||||
public void setSku(String sku) {
|
||||
this.sku = sku;
|
||||
}
|
||||
|
||||
public Vendor getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
public void setVendor(Vendor vendor) {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
public Producer getProducer() {
|
||||
return producer;
|
||||
}
|
||||
|
||||
public void setProducer(Producer producer) {
|
||||
this.producer = producer;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Vendor" )
|
||||
public static class Vendor {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "vendor", fetch = FetchType.LAZY )
|
||||
private Set<Product> products = new HashSet<>();
|
||||
|
||||
public Vendor() {
|
||||
}
|
||||
|
||||
public Vendor(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Product> getProducts() {
|
||||
return products;
|
||||
}
|
||||
|
||||
public void setProducts(Set<Product> products) {
|
||||
this.products = products;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Entity fetch scrolling
|
||||
|
||||
@Entity( name = "Resource" )
|
||||
public static class Resource {
|
||||
@Id
|
||||
@GeneratedValue( generator = "increment" )
|
||||
private Long id;
|
||||
private String name;
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
private User owner;
|
||||
|
||||
public Resource() {
|
||||
}
|
||||
|
||||
public Resource(String name, User owner) {
|
||||
this.name = name;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public User getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(User owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "User" )
|
||||
public static class User {
|
||||
@Id
|
||||
@GeneratedValue( generator = "increment" )
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Task" )
|
||||
public static class Task {
|
||||
@Id
|
||||
@GeneratedValue( generator = "increment" )
|
||||
private Long id;
|
||||
private String description;
|
||||
@ManyToOne( fetch = FetchType.LAZY)
|
||||
private User user;
|
||||
@ManyToOne( fetch = FetchType.LAZY)
|
||||
private Resource resource;
|
||||
private Date dueDate;
|
||||
private Date startDate;
|
||||
private Date completionDate;
|
||||
|
||||
public Task() {
|
||||
}
|
||||
|
||||
public Task(User user, String description, Resource resource, Date dueDate) {
|
||||
this( user, description, resource, dueDate, null, null );
|
||||
}
|
||||
|
||||
public Task(User user, String description, Resource resource, Date dueDate, Date startDate, Date completionDate) {
|
||||
this.user = user;
|
||||
this.resource = resource;
|
||||
this.description = description;
|
||||
this.dueDate = dueDate;
|
||||
this.startDate = startDate;
|
||||
this.completionDate = completionDate;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Date getDueDate() {
|
||||
return dueDate;
|
||||
}
|
||||
|
||||
public void setDueDate(Date dueDate) {
|
||||
this.dueDate = dueDate;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public Date getCompletionDate() {
|
||||
return completionDate;
|
||||
}
|
||||
|
||||
public void setCompletionDate(Date completionDate) {
|
||||
this.completionDate = completionDate;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -64,7 +64,7 @@ public class SimpleLazyGroupUpdateTest extends BaseCoreFunctionalTestCase {
|
|||
@Test
|
||||
public void test() {
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
TestEntity entity = s.load( TestEntity.class, 1L );
|
||||
TestEntity entity = s.get( TestEntity.class, 1L );
|
||||
assertLoaded( entity, "name" );
|
||||
assertNotLoaded( entity, "lifeStory" );
|
||||
assertNotLoaded( entity, "reallyBigString" );
|
||||
|
@ -76,7 +76,7 @@ public class SimpleLazyGroupUpdateTest extends BaseCoreFunctionalTestCase {
|
|||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
TestEntity entity = s.load( TestEntity.class, 1L );
|
||||
TestEntity entity = s.get( TestEntity.class, 1L );
|
||||
|
||||
assertLoaded( entity, "name" );
|
||||
assertNotLoaded( entity, "lifeStory" );
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
|
||||
@Entity
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
@Table(name = "PP_DCKey")
|
||||
public abstract class AbstractKey extends ModelEntity
|
||||
implements Serializable {
|
||||
|
||||
@Column(name = "Name")
|
||||
String name;
|
||||
|
||||
@OneToMany(targetEntity = RoleEntity.class, mappedBy = "key", fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("R")
|
||||
protected Set<RoleEntity> roles = new LinkedHashSet<>();
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("PR")
|
||||
@JoinColumn
|
||||
protected AbstractKey register;
|
||||
|
||||
@OneToMany(targetEntity = AbstractKey.class, mappedBy = "register", fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("RK")
|
||||
protected Set<AbstractKey> keys = new LinkedHashSet();
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("PP")
|
||||
@JoinColumn
|
||||
protected AbstractKey parent;
|
||||
|
||||
@OneToMany(targetEntity = AbstractKey.class, mappedBy = "parent", fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("PK")
|
||||
protected Set<AbstractKey> otherKeys = new LinkedHashSet();
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<RoleEntity> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<RoleEntity> role) {
|
||||
this.roles = role;
|
||||
}
|
||||
|
||||
public void addRole(RoleEntity role) {
|
||||
this.roles.add( role );
|
||||
}
|
||||
|
||||
public AbstractKey getRegister() {
|
||||
return register;
|
||||
}
|
||||
|
||||
public void setRegister(AbstractKey register) {
|
||||
this.register = register;
|
||||
}
|
||||
|
||||
public Set<AbstractKey> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public void setKeys(Set<AbstractKey> keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public void addRegisterKey(AbstractKey registerKey) {
|
||||
keys.add( registerKey );
|
||||
}
|
||||
|
||||
public AbstractKey getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(AbstractKey parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Set<AbstractKey> getOtherKeys() {
|
||||
return otherKeys;
|
||||
}
|
||||
|
||||
public void setOtherKeys(Set<AbstractKey> otherKeys) {
|
||||
this.otherKeys = otherKeys;
|
||||
}
|
||||
|
||||
public void addPanelKey(AbstractKey panelKey) {
|
||||
this.otherKeys.add( panelKey );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "Activity")
|
||||
@Table(name = "activity")
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class Activity extends BaseEntity {
|
||||
private String description;
|
||||
private Instruction instruction;
|
||||
|
||||
protected WebApplication webApplication = null;
|
||||
|
||||
/**
|
||||
* Used by Hibernate
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public Activity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Activity(Integer id, String description, Instruction instruction) {
|
||||
super( id );
|
||||
this.description = description;
|
||||
this.instruction = instruction;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("Instruction")
|
||||
@JoinColumn(name = "Instruction_Id")
|
||||
public Instruction getInstruction() {
|
||||
return instruction;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setInstruction(Instruction instruction) {
|
||||
this.instruction = instruction;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("webApplication")
|
||||
@JoinColumn(name="web_app_oid")
|
||||
public WebApplication getWebApplication() {
|
||||
return webApplication;
|
||||
}
|
||||
|
||||
public void setWebApplication(WebApplication webApplication) {
|
||||
this.webApplication = webApplication;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "Address")
|
||||
@Table(name = "address")
|
||||
public class Address {
|
||||
private Integer id;
|
||||
|
||||
private String text;
|
||||
|
||||
public Address() {
|
||||
}
|
||||
|
||||
public Address(Integer id, String text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column(name = "oid")
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public class BaseEntity {
|
||||
protected Integer id;
|
||||
protected String nbr;
|
||||
|
||||
public BaseEntity() {
|
||||
}
|
||||
|
||||
public BaseEntity(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column( name = "oid" )
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getNbr() {
|
||||
return nbr;
|
||||
}
|
||||
|
||||
public void setNbr(String nbr) {
|
||||
this.nbr = nbr;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-11147" )
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class BidirectionalProxyTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Test
|
||||
public void testIt() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
for (BEntity b : session.createQuery("from BEntity b", BEntity.class).getResultList()) {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
AChildEntity a = b.getA();
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
a.getVersion();
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.getStringField();
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.getIntegerField();
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.setIntegerField( 1 );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.getEntries();
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.setVersion( new Short( "2" ) );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.setStringField( "this is a string" );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
|
||||
AMappedSuperclass mappedSuperclass = a;
|
||||
mappedSuperclass.getVersion();
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
for (BEntity b : session.createQuery("from BEntity b", BEntity.class).getResultList()) {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
AChildEntity a = b.getA();
|
||||
assertEquals( "this is a string", a.getStringField() );
|
||||
assertEquals( 2, a.getVersion() );
|
||||
assertEquals( new Integer( 1 ), a.getIntegerField() );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
for (CEntity c : session.createQuery("from CEntity c", CEntity.class).getResultList()) {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
AEntity a = c.getA();
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
a.getVersion();
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.getIntegerField();
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.setIntegerField( 1 );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
a.setVersion( new Short( "2" ) );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
for (CEntity c : session.createQuery("from CEntity c", CEntity.class).getResultList()) {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
AEntity a = c.getA();
|
||||
assertEquals( 2, a.getVersion() );
|
||||
assertEquals( new Integer( 1 ), a.getIntegerField() );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( BEntity.class );
|
||||
sources.addAnnotatedClass( CEntity.class );
|
||||
sources.addAnnotatedClass( AMappedSuperclass.class );
|
||||
sources.addAnnotatedClass( AEntity.class );
|
||||
sources.addAnnotatedClass( AChildEntity.class );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
AChildEntity a = new AChildEntity("a");
|
||||
BEntity b = new BEntity("b");
|
||||
b.setA(a);
|
||||
session.persist(a);
|
||||
session.persist(b);
|
||||
|
||||
AChildEntity a1 = new AChildEntity("a1");
|
||||
CEntity c = new CEntity( "c" );
|
||||
c.setA( a1 );
|
||||
session.persist( a1 );
|
||||
session.persist( c );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearTestData(){
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from BEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from CEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from AEntity" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name="CEntity")
|
||||
@Table(name="C")
|
||||
public static class CEntity implements Serializable {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
public CEntity(String id) {
|
||||
this();
|
||||
setId(id);
|
||||
}
|
||||
|
||||
protected CEntity() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
protected void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setA(AEntity a) {
|
||||
aChildEntity = a;
|
||||
a.getcEntries().add(this);
|
||||
}
|
||||
|
||||
public AEntity getA() {
|
||||
return aChildEntity;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("aEntity")
|
||||
@JoinColumn(name="aEntity")
|
||||
protected AEntity aChildEntity = null;
|
||||
}
|
||||
|
||||
@Entity(name="BEntity")
|
||||
@Table(name="B")
|
||||
public static class BEntity implements Serializable {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
public BEntity(String id) {
|
||||
this();
|
||||
setId(id);
|
||||
}
|
||||
|
||||
protected BEntity() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
protected void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setA(AChildEntity a) {
|
||||
aChildEntity = a;
|
||||
a.getEntries().add(this);
|
||||
}
|
||||
|
||||
public AChildEntity getA() {
|
||||
return aChildEntity;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("aEntity")
|
||||
@JoinColumn(name="aEntity")
|
||||
protected AChildEntity aChildEntity = null;
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class AMappedSuperclass implements Serializable {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Basic
|
||||
private short version;
|
||||
|
||||
@Column(name = "INTEGER_FIELD")
|
||||
private Integer integerField;
|
||||
|
||||
public AMappedSuperclass(String id) {
|
||||
setId(id);
|
||||
}
|
||||
|
||||
protected AMappedSuperclass() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
protected void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public short getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(short version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Integer getIntegerField() {
|
||||
return integerField;
|
||||
}
|
||||
|
||||
public void setIntegerField(Integer integerField) {
|
||||
this.integerField = integerField;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name="AEntity")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
@Table(name="A")
|
||||
public static class AEntity extends AMappedSuperclass {
|
||||
|
||||
public AEntity(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
protected AEntity() {
|
||||
}
|
||||
|
||||
@OneToMany(targetEntity=CEntity.class, mappedBy="aChildEntity", fetch=FetchType.LAZY)
|
||||
protected Set<CEntity> cEntries = new LinkedHashSet();
|
||||
|
||||
public Set<CEntity> getcEntries() {
|
||||
return cEntries;
|
||||
}
|
||||
|
||||
public void setcEntries(Set<CEntity> cEntries) {
|
||||
this.cEntries = cEntries;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name="AChildEntity")
|
||||
@Table(name="ACChild")
|
||||
public static class AChildEntity extends AEntity {
|
||||
|
||||
private String stringField;
|
||||
|
||||
@OneToMany(targetEntity=BEntity.class, mappedBy="aChildEntity", fetch=FetchType.LAZY)
|
||||
protected Set<BEntity> entries = new LinkedHashSet();
|
||||
|
||||
public AChildEntity(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
protected AChildEntity() {
|
||||
}
|
||||
|
||||
public Set<BEntity> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public String getStringField() {
|
||||
return stringField;
|
||||
}
|
||||
|
||||
public void setStringField(String stringField) {
|
||||
this.stringField = stringField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getIntegerField() {
|
||||
return super.getIntegerField();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntegerField(Integer integerField) {
|
||||
super.setIntegerField( integerField );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "CreditCardPayment")
|
||||
@Table(name = "credit_payment")
|
||||
public class CreditCardPayment extends Payment {
|
||||
private String transactionId;
|
||||
|
||||
public CreditCardPayment() {
|
||||
}
|
||||
|
||||
public CreditCardPayment(Integer oid, Float amount, String transactionId) {
|
||||
super( oid, amount );
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public void setTransactionId(String transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "Customer")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
public abstract class Customer {
|
||||
private Integer oid;
|
||||
private String name;
|
||||
private Set<Order> orders = new HashSet<>();
|
||||
|
||||
private Address address;
|
||||
|
||||
private Customer parentCustomer;
|
||||
private Set<Customer> childCustomers = new HashSet<>();
|
||||
|
||||
public Customer() {
|
||||
}
|
||||
|
||||
public Customer(Integer oid, String name, Address address, Customer parentCustomer) {
|
||||
this.oid = oid;
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.parentCustomer = parentCustomer;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column(name = "oid")
|
||||
public Integer getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(Integer oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
|
||||
public Set<Order> getOrders() {
|
||||
return orders;
|
||||
}
|
||||
|
||||
public void setOrders(Set<Order> orders) {
|
||||
this.orders = orders;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
public Customer getParentCustomer() {
|
||||
return parentCustomer;
|
||||
}
|
||||
|
||||
public void setParentCustomer(Customer parentCustomer) {
|
||||
this.parentCustomer = parentCustomer;
|
||||
}
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parentCustomer")
|
||||
public Set<Customer> getChildCustomers() {
|
||||
return childCustomers;
|
||||
}
|
||||
|
||||
public void setChildCustomers(Set<Customer> childCustomers) {
|
||||
this.childCustomers = childCustomers;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "DebitCardPayment")
|
||||
@Table(name = "debit_payment")
|
||||
public class DebitCardPayment extends Payment {
|
||||
private String transactionId;
|
||||
|
||||
public DebitCardPayment() {
|
||||
}
|
||||
|
||||
public DebitCardPayment(Integer oid, Float amount, String transactionId) {
|
||||
super( oid, amount );
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public void setTransactionId(String transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-11147" )
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class DeepInheritanceProxyTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Test
|
||||
public void testRootGetValueToInitialize() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AEntity aEntity = session.getReference( AEntity.class, "AEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aEntity) );
|
||||
aEntity.getFieldInAMappedSuperclass();
|
||||
assertTrue( Hibernate.isInitialized( aEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AEntity aEntity = session.getReference( AEntity.class, "AEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aEntity) );
|
||||
aEntity.getFieldInAEntity();
|
||||
assertTrue( Hibernate.isInitialized( aEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootSetValueToInitialize() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AEntity aEntity = session.getReference( AEntity.class, "AEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aEntity) );
|
||||
aEntity.setFieldInAMappedSuperclass( (short) 3 );
|
||||
assertTrue( Hibernate.isInitialized( aEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AEntity aEntity = session.getReference( AEntity.class, "AEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aEntity) );
|
||||
aEntity.setFieldInAEntity( false );
|
||||
assertTrue( Hibernate.isInitialized( aEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AEntity aEntity = session.get( AEntity.class, "AEntity" );
|
||||
|
||||
assertTrue( Hibernate.isInitialized( aEntity) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMiddleGetValueToInitialize() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAEntity aaEntity = session.getReference( AAEntity.class, "AAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaEntity) );
|
||||
aaEntity.getFieldInAMappedSuperclass();
|
||||
assertTrue( Hibernate.isInitialized( aaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAEntity aaEntity = session.getReference( AAEntity.class, "AAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaEntity) );
|
||||
aaEntity.getFieldInAEntity();
|
||||
assertTrue( Hibernate.isInitialized( aaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAEntity aaEntity = session.getReference( AAEntity.class, "AAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaEntity) );
|
||||
aaEntity.getFieldInAAEntity();
|
||||
assertTrue( Hibernate.isInitialized( aaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMiddleSetValueToInitialize() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAEntity aaEntity = session.getReference( AAEntity.class, "AAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaEntity ) );
|
||||
aaEntity.setFieldInAMappedSuperclass( (short) 3 );
|
||||
assertTrue( Hibernate.isInitialized( aaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAEntity aaEntity = session.getReference( AAEntity.class, "AAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaEntity) );
|
||||
aaEntity.setFieldInAEntity( false );
|
||||
assertTrue( Hibernate.isInitialized( aaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAEntity aaEntity = session.getReference( AAEntity.class, "AAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaEntity) );
|
||||
aaEntity.setFieldInAAEntity( "updated field in AAEntity" );
|
||||
assertTrue( Hibernate.isInitialized( aaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "updated field in AAEntity", aaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAEntity aaEntity = session.get( AAEntity.class, "AAEntity" );
|
||||
|
||||
assertTrue( Hibernate.isInitialized( aaEntity) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "updated field in AAEntity", aaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeafGetValueToInitialize() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.getReference( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaaEntity) );
|
||||
aaaEntity.getFieldInAMappedSuperclass();
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 3, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.getReference( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaaEntity) );
|
||||
aaaEntity.getFieldInAEntity();
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 3, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.getReference( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaaEntity) );
|
||||
aaaEntity.getFieldInAAEntity();
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 2, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 3, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeafSetValueToInitialize() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.getReference( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaaEntity ) );
|
||||
aaaEntity.setFieldInAMappedSuperclass( (short) 3 );
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( true, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 3, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.getReference( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaaEntity) );
|
||||
aaaEntity.setFieldInAEntity( false );
|
||||
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 3, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.getReference( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaaEntity) );
|
||||
aaaEntity.setFieldInAAEntity( "updated field in AAEntity" );
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "updated field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 3, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.getReference( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertFalse( Hibernate.isInitialized( aaaEntity) );
|
||||
aaaEntity.setFieldInAAAEntity( 4 );
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity ) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "updated field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 4, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
AAAEntity aaaEntity = session.get( AAAEntity.class, "AAAEntity" );
|
||||
|
||||
assertTrue( Hibernate.isInitialized( aaaEntity) );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( (short) 3, aaaEntity.getFieldInAMappedSuperclass() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( false, aaaEntity.getFieldInAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( "updated field in AAEntity", aaaEntity.getFieldInAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertEquals( 4, aaaEntity.getFieldInAAAEntity() );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( AMappedSuperclass.class );
|
||||
sources.addAnnotatedClass( AEntity.class );
|
||||
sources.addAnnotatedClass( AAEntity.class );
|
||||
sources.addAnnotatedClass( AAAEntity.class );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
AEntity aEntity = new AEntity( "AEntity" );
|
||||
aEntity.setFieldInAMappedSuperclass( (short) 2 );
|
||||
aEntity.setFieldInAEntity( true );
|
||||
session.persist( aEntity );
|
||||
|
||||
AAEntity aaEntity = new AAAEntity( "AAEntity" );
|
||||
aaEntity.setFieldInAMappedSuperclass( (short) 2 );
|
||||
aaEntity.setFieldInAEntity( true );
|
||||
aaEntity.setFieldInAAEntity( "field in AAEntity" );
|
||||
session.persist( aaEntity );
|
||||
|
||||
AAAEntity aaaEntity = new AAAEntity( "AAAEntity" );
|
||||
aaaEntity.setFieldInAMappedSuperclass( (short) 2 );
|
||||
aaaEntity.setFieldInAEntity( true );
|
||||
aaaEntity.setFieldInAAEntity( "field in AAEntity" );
|
||||
aaaEntity.setFieldInAAAEntity( 3 );
|
||||
session.persist( aaaEntity );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearTestData(){
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from AEntity" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class AMappedSuperclass implements Serializable {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private short fieldInAMappedSuperclass;
|
||||
|
||||
public short getFieldInAMappedSuperclass() {
|
||||
return fieldInAMappedSuperclass;
|
||||
}
|
||||
|
||||
public void setFieldInAMappedSuperclass(short fieldInAMappedSuperclass) {
|
||||
this.fieldInAMappedSuperclass = fieldInAMappedSuperclass;
|
||||
}
|
||||
|
||||
public AMappedSuperclass(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
protected AMappedSuperclass() {
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name="AEntity")
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
public static class AEntity extends AMappedSuperclass {
|
||||
|
||||
private Boolean fieldInAEntity;
|
||||
|
||||
public AEntity(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
protected AEntity() {
|
||||
}
|
||||
|
||||
public Boolean getFieldInAEntity() {
|
||||
return fieldInAEntity;
|
||||
}
|
||||
public void setFieldInAEntity(Boolean fieldInAEntity) {
|
||||
this.fieldInAEntity = fieldInAEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name="AAEntity")
|
||||
public static class AAEntity extends AEntity {
|
||||
|
||||
private String fieldInAAEntity;
|
||||
|
||||
public AAEntity(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
protected AAEntity() {
|
||||
}
|
||||
|
||||
public String getFieldInAAEntity() {
|
||||
return fieldInAAEntity;
|
||||
}
|
||||
|
||||
public void setFieldInAAEntity(String fieldInAAEntity) {
|
||||
this.fieldInAAEntity = fieldInAAEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name="AAAEntity")
|
||||
public static class AAAEntity extends AAEntity {
|
||||
|
||||
private long fieldInAAAEntity;
|
||||
|
||||
public AAAEntity(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
protected AAAEntity() {
|
||||
}
|
||||
|
||||
public long getFieldInAAAEntity() {
|
||||
return fieldInAAAEntity;
|
||||
}
|
||||
|
||||
public void setFieldInAAAEntity(long fieldInAAAEntity) {
|
||||
this.fieldInAAAEntity = fieldInAAAEntity;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "DomesticCustomer")
|
||||
@Table(name = "domestic_cust")
|
||||
public class DomesticCustomer extends Customer {
|
||||
private String taxId;
|
||||
|
||||
public DomesticCustomer() {
|
||||
}
|
||||
|
||||
public DomesticCustomer(
|
||||
Integer oid,
|
||||
String name,
|
||||
String taxId,
|
||||
Address address,
|
||||
Customer parentCustomer) {
|
||||
super( oid, name, address, parentCustomer );
|
||||
this.taxId = taxId;
|
||||
}
|
||||
|
||||
public String getTaxId() {
|
||||
return taxId;
|
||||
}
|
||||
|
||||
public void setTaxId(String taxId) {
|
||||
this.taxId = taxId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-11147" )
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class EntitySharedInCollectionAndToOneTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void testIt() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
int passes = 0;
|
||||
for ( CodeTable codeTable : session.createQuery( "from CodeTable ct where ct.id = 2", CodeTable.class ).list() ) {
|
||||
assert 0 == passes;
|
||||
passes++;
|
||||
Hibernate.initialize( codeTable.getCodeTableItems() );
|
||||
}
|
||||
|
||||
assertThat( session.getPersistenceContext().getNumberOfManagedEntities(), is( 2 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void createTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final CodeTable codeTable1 = new CodeTable( 1, 1 );
|
||||
final CodeTableItem item1 = new CodeTableItem( 1, 1, "first" );
|
||||
final CodeTableItem item2 = new CodeTableItem( 2, 1, "second" );
|
||||
final CodeTableItem item3 = new CodeTableItem( 3, 1, "third" );
|
||||
|
||||
session.save( codeTable1 );
|
||||
session.save( item1 );
|
||||
session.save( item2 );
|
||||
session.save( item3 );
|
||||
|
||||
codeTable1.getCodeTableItems().add( item1 );
|
||||
item1.setCodeTable( codeTable1 );
|
||||
|
||||
codeTable1.getCodeTableItems().add( item2 );
|
||||
item2.setCodeTable( codeTable1 );
|
||||
|
||||
codeTable1.getCodeTableItems().add( item3 );
|
||||
item3.setCodeTable( codeTable1 );
|
||||
|
||||
codeTable1.setDefaultItem( item1 );
|
||||
item1.setDefaultItemInverse( codeTable1 );
|
||||
|
||||
final CodeTable codeTable2 = new CodeTable( 2, 1 );
|
||||
final CodeTableItem item4 = new CodeTableItem( 4, 1, "fourth" );
|
||||
|
||||
session.save( codeTable2 );
|
||||
session.save( item4 );
|
||||
|
||||
codeTable2.getCodeTableItems().add( item4 );
|
||||
item4.setCodeTable( codeTable2 );
|
||||
|
||||
codeTable2.setDefaultItem( item4 );
|
||||
item4.setDefaultItemInverse( codeTable2 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// @After
|
||||
// public void deleteTestData() {
|
||||
// inTransaction(
|
||||
// session -> {
|
||||
// for ( CodeTable codeTable : session.createQuery( "from CodeTable", CodeTable.class ).list() ) {
|
||||
// session.delete( codeTable );
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( CodeTableItem.class );
|
||||
sources.addAnnotatedClass( CodeTable.class );
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class BaseEntity {
|
||||
@Id
|
||||
private Integer oid;
|
||||
private int version;
|
||||
|
||||
public BaseEntity() {
|
||||
}
|
||||
|
||||
public BaseEntity(Integer oid, int version) {
|
||||
this.oid = oid;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Integer getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(Integer oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "CodeTable" )
|
||||
@Table( name = "code_table" )
|
||||
public static class CodeTable extends BaseEntity {
|
||||
@OneToOne( fetch = FetchType.LAZY )
|
||||
@LazyGroup( "defaultCodeTableItem" )
|
||||
@JoinColumn( name = "default_code_id" )
|
||||
private CodeTableItem defaultItem;
|
||||
|
||||
@OneToMany( mappedBy = "codeTable" )
|
||||
private Set<CodeTableItem> codeTableItems = new HashSet<>();
|
||||
|
||||
public CodeTable() {
|
||||
}
|
||||
|
||||
public CodeTable(Integer oid, int version) {
|
||||
super( oid, version );
|
||||
}
|
||||
|
||||
public CodeTableItem getDefaultItem() {
|
||||
return defaultItem;
|
||||
}
|
||||
|
||||
public void setDefaultItem(CodeTableItem defaultItem) {
|
||||
this.defaultItem = defaultItem;
|
||||
}
|
||||
|
||||
public Set<CodeTableItem> getCodeTableItems() {
|
||||
return codeTableItems;
|
||||
}
|
||||
|
||||
public void setCodeTableItems(Set<CodeTableItem> codeTableItems) {
|
||||
this.codeTableItems = codeTableItems;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "CodeTableItem" )
|
||||
@Table( name = "code_table_item" )
|
||||
public static class CodeTableItem extends BaseEntity {
|
||||
private String name;
|
||||
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
@LazyGroup( "codeTable" )
|
||||
@JoinColumn( name = "code_table_oid" )
|
||||
private CodeTable codeTable;
|
||||
|
||||
@OneToOne( mappedBy = "defaultItem", fetch=FetchType.LAZY )
|
||||
@LazyToOne( LazyToOneOption.NO_PROXY )
|
||||
@LazyGroup( "defaultItemInverse" )
|
||||
protected CodeTable defaultItemInverse;
|
||||
|
||||
|
||||
public CodeTableItem() {
|
||||
}
|
||||
|
||||
public CodeTableItem(Integer oid, int version, String name) {
|
||||
super( oid, version );
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public CodeTable getCodeTable() {
|
||||
return codeTable;
|
||||
}
|
||||
|
||||
public void setCodeTable(CodeTable codeTable) {
|
||||
this.codeTable = codeTable;
|
||||
}
|
||||
|
||||
public CodeTable getDefaultItemInverse() {
|
||||
return defaultItemInverse;
|
||||
}
|
||||
|
||||
public void setDefaultItemInverse(CodeTable defaultItemInverse) {
|
||||
this.defaultItemInverse = defaultItemInverse;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,926 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.sql.Blob;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings({"unused", "WeakerAccess","ResultOfMethodCallIgnored"})
|
||||
@TestForIssue( jiraKey = "HHH-11147" )
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoadNonOwningOneToOne() {
|
||||
// Test loading D and accessing E
|
||||
// E is the owner of the FK, not D. When `D#e` is accessed we
|
||||
// need to actually load E because its table has the FK value, not
|
||||
// D's table
|
||||
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
assert sessionFactory().getMetamodel()
|
||||
.entityPersister( DEntity.class )
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading();
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
final DEntity entityD = session.load( DEntity.class, 1L );
|
||||
assertThat( stats.getPrepareStatementCount(), is( 0L ) );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "a" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "c" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "e" );
|
||||
|
||||
entityD.getE();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
assert Hibernate.isPropertyInitialized( entityD, "a" );
|
||||
assert !Hibernate.isInitialized( entityD.getA() );
|
||||
assert Hibernate.isPropertyInitialized( entityD, "c" );
|
||||
assert !Hibernate.isInitialized( entityD.getC() );
|
||||
assert Hibernate.isPropertyInitialized( entityD, "e" );
|
||||
assert Hibernate.isInitialized( entityD.getE() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadOwningOneToOne() {
|
||||
// switch it around
|
||||
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
assert sessionFactory().getMetamodel()
|
||||
.entityPersister( DEntity.class )
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading();
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
final EEntity entityE = session.load( EEntity.class, 17L );
|
||||
assertThat( stats.getPrepareStatementCount(), is( 0L ) );
|
||||
assert !Hibernate.isPropertyInitialized( entityE, "d" );
|
||||
|
||||
final DEntity entityD = entityE.getD();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "a" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "c" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "e" );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchingScroll() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
assert sessionFactory().getMetamodel()
|
||||
.entityPersister( DEntity.class )
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading();
|
||||
|
||||
|
||||
inStatelessSession(
|
||||
session -> {
|
||||
final String qry = "select e from E e join fetch e.d";
|
||||
|
||||
final ScrollableResults scrollableResults = session.createQuery( qry ).scroll();
|
||||
while ( scrollableResults.next() ) {
|
||||
System.out.println( "Got entity : " + scrollableResults.get( 0 ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
inStatelessSession(
|
||||
session -> {
|
||||
final String qry = "select d from D d " +
|
||||
"join fetch d.a " +
|
||||
"join fetch d.bs " +
|
||||
"join fetch d.c " +
|
||||
"join fetch d.e " +
|
||||
"join fetch d.g";
|
||||
|
||||
final ScrollableResults scrollableResults = session.createQuery( qry ).scroll();
|
||||
while ( scrollableResults.next() ) {
|
||||
System.out.println( "Got entity : " + scrollableResults.get( 0 ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
inStatelessSession(
|
||||
session -> {
|
||||
final String qry = "select g from G g join fetch g.dEntities";
|
||||
|
||||
final ScrollableResults scrollableResults = session.createQuery( qry ).scroll();
|
||||
while ( scrollableResults.next() ) {
|
||||
System.out.println( "Got entity : " + scrollableResults.get( 0 ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLazyAssociationSameAsNonLazyInPC() {
|
||||
|
||||
assert sessionFactory().getMetamodel()
|
||||
.entityPersister( DEntity.class )
|
||||
.getInstrumentationMetadata()
|
||||
.isEnhancedForLazyLoading();
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
final AEntity entityA = session.get( AEntity.class, 1L );
|
||||
|
||||
final DEntity entityD = session.load( DEntity.class, 1L );
|
||||
assert !Hibernate.isInitialized( entityD );
|
||||
Hibernate.initialize( entityD );
|
||||
assert Hibernate.isPropertyInitialized( entityD, "a" );
|
||||
assert entityA.getOid() == entityD.getA().getOid();
|
||||
assert session.getPersistenceContext().getEntry( entityA ) ==
|
||||
session.getPersistenceContext().getEntry( entityD.getA() );
|
||||
assert entityA == entityD.getA();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomAccess() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
final EntityPersister persister = sessionFactory().getMetamodel().entityPersister( DEntity.class );
|
||||
assert persister.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Load a D
|
||||
|
||||
final DEntity entityD = session.load( DEntity.class, 1L );
|
||||
|
||||
assertThat( entityD instanceof HibernateProxy, is(false) );
|
||||
assertThat( entityD instanceof PersistentAttributeInterceptable, is(true) );
|
||||
assertThat( Hibernate.isInitialized( entityD ), is(false) );
|
||||
// Because D is enhanced we should not have executed any SQL
|
||||
assertThat( stats.getPrepareStatementCount(), is( 0L ) );
|
||||
|
||||
// access the id.
|
||||
// -since entityD is a "enhanced proxy", this should not trigger loading
|
||||
assertThat( entityD.getOid(), is(1L) );
|
||||
assertThat( Hibernate.isInitialized( entityD ), is(false) );
|
||||
assertThat( stats.getPrepareStatementCount(), is( 0L ) );
|
||||
|
||||
|
||||
// Because D is enhanced we should not have executed any SQL
|
||||
assertThat( stats.getPrepareStatementCount(), is( 0L ) );
|
||||
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "a" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "c" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "e" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "g" );
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Access C
|
||||
|
||||
final CEntity c = entityD.getC();
|
||||
|
||||
// make sure interception happened
|
||||
assertThat( c, notNullValue() );
|
||||
|
||||
// See `#testLoadNonOwningOneToOne`
|
||||
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
// The fields themselves are initialized - set to the
|
||||
// enhanced entity "proxy" instance
|
||||
assert Hibernate.isPropertyInitialized( entityD, "a" );
|
||||
assert Hibernate.isPropertyInitialized( entityD, "c" );
|
||||
assert !Hibernate.isPropertyInitialized( entityD, "e" );
|
||||
|
||||
assert !Hibernate.isInitialized( entityD.getA() );
|
||||
assert !Hibernate.isInitialized( entityD.getC() );
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Access C again
|
||||
|
||||
entityD.getC();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Access E
|
||||
|
||||
final EEntity e1 = entityD.getE();
|
||||
assert Hibernate.isPropertyInitialized( entityD, "e" );
|
||||
|
||||
assert Hibernate.isInitialized( entityD.getE() );
|
||||
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
assert Hibernate.isInitialized( e1 );
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Access E again
|
||||
|
||||
entityD.getE();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
|
||||
assertThat( entityD.getE().getOid(), is(17L) );
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// now lets access the attribute "proxies"
|
||||
|
||||
// this will load the table C data
|
||||
entityD.getC().getC1();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 3L ) );
|
||||
assert Hibernate.isInitialized( entityD.getC() );
|
||||
|
||||
// this should not - it was already loaded above
|
||||
entityD.getE().getE1();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 3L ) );
|
||||
|
||||
Set<BEntity> bs = entityD.getBs();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 3L ) );
|
||||
assertThat( bs.size(), is( 2 ) );
|
||||
assertThat( stats.getPrepareStatementCount(), is( 4L ) );
|
||||
|
||||
entityD.getG().getOid();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 4L ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullManyToOneHql() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final String qry = "select e from Activity e";
|
||||
final List<Activity> activities = session.createQuery( qry, Activity.class ).list();
|
||||
|
||||
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
long expectedCount = 1L;
|
||||
|
||||
for ( Activity activity : activities ) {
|
||||
if ( activity.getInstruction() != null ) {
|
||||
// do something special
|
||||
// - here we just access an attribute to trigger
|
||||
// the initialization of the association
|
||||
|
||||
activity.getInstruction().getSummary();
|
||||
assertThat( stats.getPrepareStatementCount(), is( ++expectedCount ) );
|
||||
}
|
||||
|
||||
if ( activity.getWebApplication() != null ) {
|
||||
// trigger base group initialization
|
||||
activity.getWebApplication().getName();
|
||||
assertThat( stats.getPrepareStatementCount(), is( ++expectedCount ) );
|
||||
// trigger initialization
|
||||
activity.getWebApplication().getSiteUrl();
|
||||
assertThat( stats.getPrepareStatementCount(), is( ++expectedCount ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbstractClassAssociation() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
assert sessionFactory().getMetamodel()
|
||||
.entityPersister( RoleEntity.class )
|
||||
.getInstrumentationMetadata()
|
||||
.isEnhancedForLazyLoading();
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final String qry = "select e from RoleEntity e";
|
||||
final List<RoleEntity> keyRoleEntities = session.createQuery( qry, RoleEntity.class ).list();
|
||||
|
||||
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
for ( RoleEntity keyRoleEntity : keyRoleEntities ) {
|
||||
Object key = Hibernate.unproxy( keyRoleEntity.getKey() );
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
|
||||
Set<SpecializedEntity> specializedEntities = ( (SpecializedKey) key )
|
||||
.getSpecializedEntities();
|
||||
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
|
||||
Iterator<SpecializedEntity> iterator = specializedEntities.iterator();
|
||||
while ( iterator.hasNext() ) {
|
||||
SpecializedEntity specializedEntity = iterator.next();
|
||||
assertThat( specializedEntity.getId(), notNullValue() );
|
||||
specializedEntity.getValue();
|
||||
}
|
||||
|
||||
// but regardless there should not be an additional query
|
||||
assertThat( stats.getPrepareStatementCount(), is( 3L ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonAbstractAssociationWithSubclassValue() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final String qry = "select e from RoleEntity e";
|
||||
final List<RoleEntity> keyRoleEntities = session.createQuery( qry, RoleEntity.class ).list();
|
||||
|
||||
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
assertThat( keyRoleEntities.size(), is( 1 ) );
|
||||
|
||||
RoleEntity roleEntity = keyRoleEntities.get( 0 );
|
||||
assertThat(
|
||||
Hibernate.unproxy( roleEntity.getKey() ).getClass().getName(),
|
||||
is( SpecializedKey.class.getName() )
|
||||
);
|
||||
|
||||
assertThat(
|
||||
Hibernate.unproxy( roleEntity.getSpecializedKey() ).getClass().getName(),
|
||||
is( MoreSpecializedKey.class.getName() )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAndDeleteDEntity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
List<DEntity> result = session.createQuery(
|
||||
"select d from D d ",
|
||||
DEntity.class
|
||||
).list();
|
||||
result.forEach( entity -> {
|
||||
session.delete( entity );
|
||||
session.delete( entity.getE() );
|
||||
session.delete( entity.getA() );
|
||||
Set<BEntity> bs = entity.getBs();
|
||||
bs.forEach( bEntity -> session.delete( bEntity ) );
|
||||
session.delete( entity.getC() );
|
||||
session.delete( entity.getG() );
|
||||
|
||||
} );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadAndDeleteDEntity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
DEntity entity = session.load( DEntity.class, 1L );
|
||||
session.delete( entity );
|
||||
session.delete( entity.getE() );
|
||||
session.delete( entity.getA() );
|
||||
Set<BEntity> bs = entity.getBs();
|
||||
bs.forEach( bEntity -> session.delete( bEntity ) );
|
||||
session.delete( entity.getC() );
|
||||
session.delete( entity.getG() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAndDeleteDEntity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
DEntity entity = session.get( DEntity.class, 1L );
|
||||
session.delete( entity );
|
||||
session.delete( entity.getE() );
|
||||
session.delete( entity.getA() );
|
||||
Set<BEntity> bs = entity.getBs();
|
||||
bs.forEach( bEntity -> session.delete( bEntity ) );
|
||||
session.delete( entity.getC() );
|
||||
session.delete( entity.getG() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAndDeleteEEntity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
EEntity entity = session.get( EEntity.class, 17L );
|
||||
session.delete( entity );
|
||||
session.delete( entity.getD() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadAndDeleteEEntity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
EEntity entity = session.load( EEntity.class, 17L );
|
||||
session.delete( entity );
|
||||
session.delete( entity.getD() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAndDeleteEEntity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
List<EEntity> result = session.createQuery(
|
||||
"select e from E e",
|
||||
EEntity.class
|
||||
).list();
|
||||
result.forEach( entity -> {
|
||||
session.delete( entity );
|
||||
session.delete( entity.getD() );
|
||||
} );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( AEntity.class );
|
||||
sources.addAnnotatedClass( BEntity.class );
|
||||
sources.addAnnotatedClass( CEntity.class );
|
||||
sources.addAnnotatedClass( DEntity.class );
|
||||
sources.addAnnotatedClass( EEntity.class );
|
||||
sources.addAnnotatedClass( GEntity.class );
|
||||
|
||||
sources.addAnnotatedClass( Activity.class );
|
||||
sources.addAnnotatedClass( Instruction.class );
|
||||
sources.addAnnotatedClass( WebApplication.class );
|
||||
|
||||
sources.addAnnotatedClass( SpecializedKey.class );
|
||||
sources.addAnnotatedClass( MoreSpecializedKey.class );
|
||||
sources.addAnnotatedClass( RoleEntity.class );
|
||||
sources.addAnnotatedClass( AbstractKey.class );
|
||||
sources.addAnnotatedClass( GenericKey.class );
|
||||
sources.addAnnotatedClass( SpecializedEntity.class );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
DEntity d = new DEntity();
|
||||
d.setD( "bla" );
|
||||
d.setOid( 1 );
|
||||
|
||||
byte[] lBytes = "agdfagdfagfgafgsfdgasfdgfgasdfgadsfgasfdgasfdgasdasfdg".getBytes();
|
||||
Blob lBlob = Hibernate.getLobCreator( session ).createBlob( lBytes );
|
||||
d.setBlob( lBlob );
|
||||
|
||||
BEntity b1 = new BEntity();
|
||||
b1.setOid( 1 );
|
||||
b1.setB1( 34 );
|
||||
b1.setB2( "huhu" );
|
||||
|
||||
BEntity b2 = new BEntity();
|
||||
b2.setOid( 2 );
|
||||
b2.setB1( 37 );
|
||||
b2.setB2( "haha" );
|
||||
|
||||
Set<BEntity> lBs = new HashSet<>();
|
||||
lBs.add( b1 );
|
||||
lBs.add( b2 );
|
||||
d.setBs( lBs );
|
||||
|
||||
AEntity a = new AEntity();
|
||||
a.setOid( 1 );
|
||||
a.setA( "hihi" );
|
||||
d.setA( a );
|
||||
|
||||
EEntity e = new EEntity();
|
||||
e.setOid( 17 );
|
||||
e.setE1( "Balu" );
|
||||
e.setE2( "Bär" );
|
||||
|
||||
e.setD( d );
|
||||
d.setE( e );
|
||||
|
||||
CEntity c = new CEntity();
|
||||
c.setOid( 1 );
|
||||
c.setC1( "ast" );
|
||||
c.setC2( "qwert" );
|
||||
c.setC3( "yxcv" );
|
||||
d.setC( c );
|
||||
|
||||
GEntity g = new GEntity();
|
||||
g.setOid( 1 );
|
||||
g.getdEntities().add( d );
|
||||
d.setG( g );
|
||||
|
||||
|
||||
session.save( b1 );
|
||||
session.save( b2 );
|
||||
session.save( a );
|
||||
session.save( c );
|
||||
session.save( g );
|
||||
session.save( d );
|
||||
session.save( e );
|
||||
|
||||
|
||||
// create a slew of Activity objects, some with Instruction reference
|
||||
// some without.
|
||||
|
||||
for ( int i = 0; i < 30; i++ ) {
|
||||
final Activity activity = new Activity( i, "Activity #" + i, null );
|
||||
if ( i % 2 == 0 ) {
|
||||
final Instruction instr = new Instruction( i, "Instruction #" + i );
|
||||
activity.setInstruction( instr );
|
||||
session.save( instr );
|
||||
}
|
||||
else {
|
||||
final WebApplication webApplication = new WebApplication( i, "http://" + i + ".com" );
|
||||
activity.setWebApplication( webApplication );
|
||||
webApplication.getActivities().add( activity );
|
||||
session.save( webApplication );
|
||||
}
|
||||
|
||||
session.save( activity );
|
||||
}
|
||||
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
roleEntity.setOid( 1L );
|
||||
|
||||
SpecializedKey specializedKey = new SpecializedKey();
|
||||
specializedKey.setOid(1L);
|
||||
|
||||
MoreSpecializedKey moreSpecializedKey = new MoreSpecializedKey();
|
||||
moreSpecializedKey.setOid( 3L );
|
||||
|
||||
SpecializedEntity specializedEntity = new SpecializedEntity();
|
||||
specializedEntity.setId( 2L );
|
||||
specializedKey.addSpecializedEntity( specializedEntity );
|
||||
specializedEntity.setSpecializedKey( specializedKey);
|
||||
|
||||
specializedKey.addRole( roleEntity );
|
||||
roleEntity.setKey( specializedKey );
|
||||
roleEntity.setSpecializedKey( moreSpecializedKey );
|
||||
moreSpecializedKey.addRole( roleEntity );
|
||||
session.save( specializedEntity );
|
||||
session.save( roleEntity );
|
||||
session.save( specializedKey );
|
||||
session.save( moreSpecializedKey );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from E" ).executeUpdate();
|
||||
session.createQuery( "delete from D" ).executeUpdate();
|
||||
session.createQuery( "delete from C" ).executeUpdate();
|
||||
session.createQuery( "delete from B" ).executeUpdate();
|
||||
session.createQuery( "delete from A" ).executeUpdate();
|
||||
session.createQuery( "delete from G" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from Activity" ).executeUpdate();
|
||||
session.createQuery( "delete from Instruction" ).executeUpdate();
|
||||
session.createQuery( "delete from WebApplication" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from SpecializedEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from RoleEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from MoreSpecializedKey" ).executeUpdate();
|
||||
session.createQuery( "delete from SpecializedKey" ).executeUpdate();
|
||||
session.createQuery( "delete from GenericKey" ).executeUpdate();
|
||||
session.createQuery( "delete from AbstractKey" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class BaseEntity {
|
||||
@Id
|
||||
private long oid;
|
||||
private short version;
|
||||
|
||||
public long getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(long oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public short getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(short version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "A")
|
||||
@Table(name = "A")
|
||||
public static class AEntity extends BaseEntity {
|
||||
@Column(name = "A")
|
||||
private String a;
|
||||
|
||||
public String getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(String a) {
|
||||
this.a = a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Entity(name = "B")
|
||||
@Table(name = "B")
|
||||
public static class BEntity extends BaseEntity {
|
||||
private Integer b1;
|
||||
private String b2;
|
||||
|
||||
public Integer getB1() {
|
||||
return b1;
|
||||
}
|
||||
|
||||
public void setB1(Integer b1) {
|
||||
this.b1 = b1;
|
||||
}
|
||||
|
||||
public String getB2() {
|
||||
return b2;
|
||||
}
|
||||
|
||||
public void setB2(String b2) {
|
||||
this.b2 = b2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Entity(name = "C")
|
||||
@Table(name = "C")
|
||||
public static class CEntity extends BaseEntity {
|
||||
private String c1;
|
||||
private String c2;
|
||||
private String c3;
|
||||
private Long c4;
|
||||
|
||||
public String getC1() {
|
||||
return c1;
|
||||
}
|
||||
|
||||
public void setC1(String c1) {
|
||||
this.c1 = c1;
|
||||
}
|
||||
|
||||
public String getC2() {
|
||||
return c2;
|
||||
}
|
||||
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
public void setC2(String c2) {
|
||||
this.c2 = c2;
|
||||
}
|
||||
|
||||
public String getC3() {
|
||||
return c3;
|
||||
}
|
||||
|
||||
public void setC3(String c3) {
|
||||
this.c3 = c3;
|
||||
}
|
||||
|
||||
public Long getC4() {
|
||||
return c4;
|
||||
}
|
||||
|
||||
public void setC4(Long c4) {
|
||||
this.c4 = c4;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "D")
|
||||
@Table(name = "D")
|
||||
public static class DEntity extends BaseEntity {
|
||||
private String d;
|
||||
|
||||
// ****** Relations *****************
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
// @LazyToOne(LazyToOneOption.PROXY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("a")
|
||||
public AEntity a;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
// @LazyToOne(LazyToOneOption.PROXY)
|
||||
@LazyGroup("c")
|
||||
public CEntity c;
|
||||
|
||||
@OneToMany(targetEntity = BEntity.class)
|
||||
public Set<BEntity> bs;
|
||||
|
||||
@OneToOne(mappedBy = "d", fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("e")
|
||||
private EEntity e;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn()
|
||||
@LazyGroup("g")
|
||||
public GEntity g;
|
||||
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
@LazyGroup("blob")
|
||||
private Blob blob;
|
||||
|
||||
public String getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
public void setD(String d) {
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
|
||||
public AEntity getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(AEntity a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Set<BEntity> getBs() {
|
||||
return bs;
|
||||
}
|
||||
|
||||
public void setBs(Set<BEntity> bs) {
|
||||
this.bs = bs;
|
||||
}
|
||||
|
||||
public CEntity getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
public void setC(CEntity c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public Blob getBlob() {
|
||||
return blob;
|
||||
}
|
||||
|
||||
public void setBlob(Blob blob) {
|
||||
this.blob = blob;
|
||||
}
|
||||
|
||||
public EEntity getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
public void setE(EEntity e) {
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
public GEntity getG() {
|
||||
return g;
|
||||
}
|
||||
|
||||
public void setG(GEntity g) {
|
||||
this.g = g;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "E")
|
||||
@Table(name = "E")
|
||||
public static class EEntity extends BaseEntity {
|
||||
private String e1;
|
||||
private String e2;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
private DEntity d;
|
||||
|
||||
public String getE1() {
|
||||
return e1;
|
||||
}
|
||||
|
||||
public void setE1(String e1) {
|
||||
this.e1 = e1;
|
||||
}
|
||||
|
||||
public String getE2() {
|
||||
return e2;
|
||||
}
|
||||
|
||||
public void setE2(String e2) {
|
||||
this.e2 = e2;
|
||||
}
|
||||
|
||||
public DEntity getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
public void setD(DEntity d) {
|
||||
this.d = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "G")
|
||||
@Table(name = "G")
|
||||
public static class GEntity extends BaseEntity {
|
||||
|
||||
@OneToMany(mappedBy = "g")
|
||||
public Set<DEntity> dEntities = new HashSet<>();
|
||||
|
||||
public Set<DEntity> getdEntities() {
|
||||
return dEntities;
|
||||
}
|
||||
|
||||
public void setdEntities(Set<DEntity> dEntities) {
|
||||
this.dEntities = dEntities;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "ForeignCustomer")
|
||||
@Table(name = "foreign_cust")
|
||||
public class ForeignCustomer extends Customer {
|
||||
private String vat;
|
||||
|
||||
public ForeignCustomer() {
|
||||
}
|
||||
|
||||
public ForeignCustomer(
|
||||
Integer oid,
|
||||
String name,
|
||||
Address address,
|
||||
String vat,
|
||||
Customer parentCustomer) {
|
||||
super( oid, name, address, parentCustomer );
|
||||
this.vat = vat;
|
||||
}
|
||||
|
||||
public ForeignCustomer(
|
||||
Integer oid,
|
||||
String name,
|
||||
Address address,
|
||||
String vat) {
|
||||
this( oid, name, address, vat, null );
|
||||
}
|
||||
|
||||
public String getVat() {
|
||||
return vat;
|
||||
}
|
||||
|
||||
public void setVat(String vat) {
|
||||
this.vat = vat;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity(name="GenericKey")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
@Table(name="PP_GenericDCKey")
|
||||
public abstract class GenericKey extends AbstractKey implements Serializable {
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "Instruction")
|
||||
@Table(name = "instruction")
|
||||
public class Instruction extends BaseEntity {
|
||||
private String summary;
|
||||
|
||||
/**
|
||||
* Used by Hibernate
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public Instruction() {
|
||||
super();
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public Instruction(Integer id, String summary) {
|
||||
super( id );
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-11147")
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class LazyGroupWithInheritanceAllowProxyTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void baseline() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final List<Order> orders = session.createQuery( "select o from Order o", Order.class ).list();
|
||||
for ( Order order : orders ) {
|
||||
if ( order.getCustomer().getOid() == null ) {
|
||||
System.out.println( "Got Order#customer: " + order.getCustomer().getOid() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergingUninitializedProxy() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final List<Order> orders = session.createQuery( "select o from Order o", Order.class ).list();
|
||||
for ( Order order : orders ) {
|
||||
System.out.println( "Got Order#customer: " + order.getCustomer().getOid() );
|
||||
assertThat( order.getCustomer().getAddress(), notNullValue() );
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) order.getCustomer().getAddress();
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
assertThat( interceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void queryEntityWithAssociationToAbstract() {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
final AtomicInteger expectedQueryCount = new AtomicInteger( 0 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final List<Order> orders = session.createQuery( "select o from Order o", Order.class ).list();
|
||||
|
||||
expectedQueryCount.set( 1 );
|
||||
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
for ( Order order : orders ) {
|
||||
System.out.println( "############################################" );
|
||||
System.out.println( "Starting Order #" + order.getOid() );
|
||||
|
||||
// accessing the many-to-one's id should not trigger a load
|
||||
if ( order.getCustomer().getOid() == null ) {
|
||||
System.out.println( "Got Order#customer: " + order.getCustomer().getOid() );
|
||||
}
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// accessing the one-to-many should trigger a load
|
||||
final Set<Payment> orderPayments = order.getPayments();
|
||||
System.out.println( "Number of payments = " + orderPayments.size() );
|
||||
expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// access the non-inverse, logical 1-1
|
||||
order.getSupplemental();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental() != null ) {
|
||||
System.out.println( "Got Order#supplemental = " + order.getSupplemental().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
|
||||
// access the inverse, logical 1-1
|
||||
order.getSupplemental2();
|
||||
expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental2() != null ) {
|
||||
System.out.println( "Got Order#supplemental2 = " + order.getSupplemental2().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same test as {@link #queryEntityWithAssociationToAbstract()}, but using runtime
|
||||
* fetching to issues just a single select
|
||||
*/
|
||||
@Test
|
||||
public void queryEntityWithAssociationToAbstractRuntimeFetch() {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
final AtomicInteger expectedQueryCount = new AtomicInteger( 0 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final String qry = "select o from Order o join fetch o.customer c join fetch o.payments join fetch o.supplemental join fetch o.supplemental2";
|
||||
|
||||
final List<Order> orders = session.createQuery( qry, Order.class ).list();
|
||||
|
||||
// oh look - just a single query for all the data we will need. hmm, crazy
|
||||
expectedQueryCount.set( 1 );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
for ( Order order : orders ) {
|
||||
System.out.println( "############################################" );
|
||||
System.out.println( "Starting Order #" + order.getOid() );
|
||||
|
||||
// accessing the many-to-one's id should not trigger a load
|
||||
if ( order.getCustomer().getOid() == null ) {
|
||||
System.out.println( "Got Order#customer: " + order.getCustomer().getOid() );
|
||||
}
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// accessing the one-to-many should trigger a load
|
||||
final Set<Payment> orderPayments = order.getPayments();
|
||||
System.out.println( "Number of payments = " + orderPayments.size() );
|
||||
|
||||
// loaded already
|
||||
// expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// access the non-inverse, logical 1-1
|
||||
order.getSupplemental();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental() != null ) {
|
||||
System.out.println( "Got Order#supplemental = " + order.getSupplemental().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
|
||||
// access the inverse, logical 1-1
|
||||
order.getSupplemental2();
|
||||
|
||||
// loaded already
|
||||
// expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental2() != null ) {
|
||||
System.out.println( "Got Order#supplemental2 = " + order.getSupplemental2().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
|
||||
// todo : this is the only difference between this test and LazyGroupWithInheritanceTest
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
|
||||
ssrb.applySetting( AvailableSettings.USE_QUERY_CACHE, "false" );
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Address austin = new Address( 1, "Austin" );
|
||||
final Address london = new Address( 2, "London" );
|
||||
|
||||
session.save( austin );
|
||||
session.save( london );
|
||||
|
||||
final ForeignCustomer acme = new ForeignCustomer( 1, "Acme", london, "1234" );
|
||||
final ForeignCustomer acmeBrick = new ForeignCustomer( 2, "Acme Brick", london, "9876", acme );
|
||||
|
||||
final ForeignCustomer freeBirds = new ForeignCustomer( 3, "Free Birds", austin, "13579" );
|
||||
|
||||
session.save( acme );
|
||||
session.save( acmeBrick );
|
||||
session.save( freeBirds );
|
||||
|
||||
final Order order1 = new Order( 1, "some text", freeBirds );
|
||||
freeBirds.getOrders().add( order1 );
|
||||
session.save( order1 );
|
||||
|
||||
final OrderSupplemental orderSupplemental = new OrderSupplemental( 1, 1 );
|
||||
order1.setSupplemental( orderSupplemental );
|
||||
final OrderSupplemental2 orderSupplemental2_1 = new OrderSupplemental2( 2, 2 );
|
||||
order1.setSupplemental2( orderSupplemental2_1 );
|
||||
orderSupplemental2_1.setOrder( order1 );
|
||||
session.save( orderSupplemental );
|
||||
session.save( orderSupplemental2_1 );
|
||||
|
||||
final Order order2 = new Order( 2, "some text", acme );
|
||||
acme.getOrders().add( order2 );
|
||||
session.save( order2 );
|
||||
|
||||
final OrderSupplemental2 orderSupplemental2_2 = new OrderSupplemental2( 3, 3 );
|
||||
order2.setSupplemental2( orderSupplemental2_2 );
|
||||
orderSupplemental2_2.setOrder( order2 );
|
||||
session.save( orderSupplemental2_2 );
|
||||
|
||||
final CreditCardPayment payment1 = new CreditCardPayment( 1, 1F, "1" );
|
||||
session.save( payment1 );
|
||||
order1.getPayments().add( payment1 );
|
||||
|
||||
final DebitCardPayment payment2 = new DebitCardPayment( 2, 2F, "2" );
|
||||
session.save( payment2 );
|
||||
order1.getPayments().add( payment2 );
|
||||
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from CreditCardPayment" ).executeUpdate();
|
||||
session.createQuery( "delete from DebitCardPayment" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from OrderSupplemental2" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from Order" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from OrderSupplemental" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from DomesticCustomer" ).executeUpdate();
|
||||
session.createQuery( "delete from ForeignCustomer" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from Address" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
|
||||
sources.addAnnotatedClass( Customer.class );
|
||||
sources.addAnnotatedClass( ForeignCustomer.class );
|
||||
sources.addAnnotatedClass( DomesticCustomer.class );
|
||||
|
||||
sources.addAnnotatedClass( Payment.class );
|
||||
sources.addAnnotatedClass( CreditCardPayment.class );
|
||||
sources.addAnnotatedClass( DebitCardPayment.class );
|
||||
|
||||
sources.addAnnotatedClass( Address.class );
|
||||
|
||||
sources.addAnnotatedClass( Order.class );
|
||||
sources.addAnnotatedClass( OrderSupplemental.class );
|
||||
sources.addAnnotatedClass( OrderSupplemental2.class );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.hibernate.test.bytecode.enhancement.lazy.group.BidirectionalLazyGroupsInEmbeddableTest;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-11147")
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class LazyGroupWithInheritanceTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void queryEntityWithAssociationToAbstract() {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
final AtomicInteger expectedQueryCount = new AtomicInteger( 0 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final List<Order> orders = session.createQuery( "select o from Order o", Order.class ).list();
|
||||
|
||||
// todo (HHH-11147) : this is a regression from 4.x
|
||||
// - the condition is that the association from Order to Customer points to the non-root
|
||||
// entity (Customer) rather than one of its concrete sub-types (DomesticCustomer,
|
||||
// ForeignCustomer). We'd have to read the "other table" to be able to resolve the
|
||||
// concrete type. The same holds true for associations to versioned entities as well.
|
||||
// The only viable solution I see would be to join to the "other side" and read the
|
||||
// version/discriminator[1]. But of course that means doing the join which is generally
|
||||
// what the application is trying to avoid in the first place
|
||||
//expectedQueryCount.set( 1 );
|
||||
expectedQueryCount.set( 4 );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
for ( Order order : orders ) {
|
||||
System.out.println( "############################################" );
|
||||
System.out.println( "Starting Order #" + order.getOid() );
|
||||
|
||||
// accessing the many-to-one's id should not trigger a load
|
||||
if ( order.getCustomer().getOid() == null ) {
|
||||
System.out.println( "Got Order#customer: " + order.getCustomer().getOid() );
|
||||
}
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// accessing the one-to-many should trigger a load
|
||||
final Set<Payment> orderPayments = order.getPayments();
|
||||
System.out.println( "Number of payments = " + orderPayments.size() );
|
||||
expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// access the non-inverse, logical 1-1
|
||||
order.getSupplemental();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental() != null ) {
|
||||
System.out.println( "Got Order#supplemental = " + order.getSupplemental().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
|
||||
// access the inverse, logical 1-1
|
||||
order.getSupplemental2();
|
||||
expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental2() != null ) {
|
||||
System.out.println( "Got Order#supplemental2 = " + order.getSupplemental2().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same test as {@link #queryEntityWithAssociationToAbstract()}, but using runtime
|
||||
* fetching to issues just a single select
|
||||
*/
|
||||
@Test
|
||||
public void queryEntityWithAssociationToAbstractRuntimeFetch() {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
final AtomicInteger expectedQueryCount = new AtomicInteger( 0 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final String qry = "select o from Order o join fetch o.customer c join fetch o.payments join fetch o.supplemental join fetch o.supplemental2";
|
||||
|
||||
final List<Order> orders = session.createQuery( qry, Order.class ).list();
|
||||
|
||||
// oh look - just a single query for all the data we will need. hmm, crazy
|
||||
expectedQueryCount.set( 1 );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
for ( Order order : orders ) {
|
||||
System.out.println( "############################################" );
|
||||
System.out.println( "Starting Order #" + order.getOid() );
|
||||
|
||||
// accessing the many-to-one's id should not trigger a load
|
||||
if ( order.getCustomer().getOid() == null ) {
|
||||
System.out.println( "Got Order#customer: " + order.getCustomer().getOid() );
|
||||
}
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// accessing the one-to-many should trigger a load
|
||||
final Set<Payment> orderPayments = order.getPayments();
|
||||
System.out.println( "Number of payments = " + orderPayments.size() );
|
||||
|
||||
// loaded already
|
||||
// expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
|
||||
// access the non-inverse, logical 1-1
|
||||
order.getSupplemental();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental() != null ) {
|
||||
System.out.println( "Got Order#supplemental = " + order.getSupplemental().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
|
||||
// access the inverse, logical 1-1
|
||||
order.getSupplemental2();
|
||||
|
||||
// loaded already
|
||||
// expectedQueryCount.getAndIncrement();
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
if ( order.getSupplemental2() != null ) {
|
||||
System.out.println( "Got Order#supplemental2 = " + order.getSupplemental2().getOid() );
|
||||
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
|
||||
ssrb.applySetting( AvailableSettings.USE_QUERY_CACHE, "false" );
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Address austin = new Address( 1, "Austin" );
|
||||
final Address london = new Address( 2, "London" );
|
||||
|
||||
session.save( austin );
|
||||
session.save( london );
|
||||
|
||||
final ForeignCustomer acme = new ForeignCustomer( 1, "Acme", london, "1234" );
|
||||
final ForeignCustomer acmeBrick = new ForeignCustomer( 2, "Acme Brick", london, "9876", acme );
|
||||
|
||||
final ForeignCustomer freeBirds = new ForeignCustomer( 3, "Free Birds", austin, "13579" );
|
||||
|
||||
session.save( acme );
|
||||
session.save( acmeBrick );
|
||||
session.save( freeBirds );
|
||||
|
||||
final Order order1 = new Order( 1, "some text", freeBirds );
|
||||
freeBirds.getOrders().add( order1 );
|
||||
session.save( order1 );
|
||||
|
||||
final OrderSupplemental orderSupplemental = new OrderSupplemental( 1, 1 );
|
||||
order1.setSupplemental( orderSupplemental );
|
||||
final OrderSupplemental2 orderSupplemental2_1 = new OrderSupplemental2( 2, 2 );
|
||||
order1.setSupplemental2( orderSupplemental2_1 );
|
||||
orderSupplemental2_1.setOrder( order1 );
|
||||
session.save( orderSupplemental );
|
||||
session.save( orderSupplemental2_1 );
|
||||
|
||||
final Order order2 = new Order( 2, "some text", acme );
|
||||
acme.getOrders().add( order2 );
|
||||
session.save( order2 );
|
||||
|
||||
final OrderSupplemental2 orderSupplemental2_2 = new OrderSupplemental2( 3, 3 );
|
||||
order2.setSupplemental2( orderSupplemental2_2 );
|
||||
orderSupplemental2_2.setOrder( order2 );
|
||||
session.save( orderSupplemental2_2 );
|
||||
|
||||
final CreditCardPayment payment1 = new CreditCardPayment( 1, 1F, "1" );
|
||||
session.save( payment1 );
|
||||
order1.getPayments().add( payment1 );
|
||||
|
||||
final DebitCardPayment payment2 = new DebitCardPayment( 2, 2F, "2" );
|
||||
session.save( payment2 );
|
||||
order1.getPayments().add( payment2 );
|
||||
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from CreditCardPayment" ).executeUpdate();
|
||||
session.createQuery( "delete from DebitCardPayment" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from OrderSupplemental2" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from Order" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from OrderSupplemental" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from DomesticCustomer" ).executeUpdate();
|
||||
session.createQuery( "delete from ForeignCustomer" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from Address" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
|
||||
sources.addAnnotatedClass( Customer.class );
|
||||
sources.addAnnotatedClass( ForeignCustomer.class );
|
||||
sources.addAnnotatedClass( DomesticCustomer.class );
|
||||
|
||||
sources.addAnnotatedClass( Payment.class );
|
||||
sources.addAnnotatedClass( CreditCardPayment.class );
|
||||
sources.addAnnotatedClass( DebitCardPayment.class );
|
||||
|
||||
sources.addAnnotatedClass( Address.class );
|
||||
|
||||
sources.addAnnotatedClass( Order.class );
|
||||
sources.addAnnotatedClass( OrderSupplemental.class );
|
||||
sources.addAnnotatedClass( OrderSupplemental2.class );
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-11147" )
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class MergeDetachedToProxyTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Test
|
||||
public void testMergeInitializedDetachedOntoProxy() {
|
||||
final StatisticsImplementor statistics = sessionFactory().getStatistics();
|
||||
|
||||
final AEntity aEntityDetached = fromTransaction(
|
||||
session -> {
|
||||
AEntity aEntity = session.get( AEntity.class, 1 );
|
||||
assertIsEnhancedProxy( aEntity.bEntity );
|
||||
Hibernate.initialize( aEntity.bEntity );
|
||||
return aEntity;
|
||||
}
|
||||
);
|
||||
|
||||
statistics.clear();
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 0L ) );
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
BEntity bEntity = session.getReference( BEntity.class, 2 );
|
||||
assertIsEnhancedProxy( bEntity );
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 0L ) );
|
||||
|
||||
AEntity aEntityMerged = (AEntity) session.merge( aEntityDetached );
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
assertSame( bEntity, aEntityMerged.bEntity );
|
||||
assertEquals( "a description", aEntityDetached.bEntity.description );
|
||||
assertTrue( Hibernate.isInitialized( bEntity ) );
|
||||
}
|
||||
);
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeUpdatedDetachedOntoProxy() {
|
||||
final StatisticsImplementor statistics = sessionFactory().getStatistics();
|
||||
|
||||
final AEntity aEntityDetached = fromTransaction(
|
||||
session -> {
|
||||
AEntity aEntity = session.get( AEntity.class, 1 );
|
||||
assertIsEnhancedProxy( aEntity.bEntity );
|
||||
Hibernate.initialize( aEntity.bEntity );
|
||||
return aEntity;
|
||||
}
|
||||
);
|
||||
|
||||
aEntityDetached.bEntity.description = "new description";
|
||||
|
||||
statistics.clear();
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 0L ) );
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
BEntity bEntity = session.getReference( BEntity.class, 2 );
|
||||
assertIsEnhancedProxy( bEntity );
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 0L ) );
|
||||
|
||||
AEntity aEntityMerged = (AEntity) session.merge( aEntityDetached );
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
assertSame( bEntity, aEntityMerged.bEntity );
|
||||
assertEquals( "new description", aEntityDetached.bEntity.description );
|
||||
assertTrue( Hibernate.isInitialized( bEntity ) );
|
||||
}
|
||||
);
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( AEntity.class );
|
||||
sources.addAnnotatedClass( BEntity.class );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
final AEntity aEntity = new AEntity();
|
||||
aEntity.id = 1;
|
||||
final BEntity bEntity = new BEntity();
|
||||
bEntity.id = 2;
|
||||
bEntity.description = "a description";
|
||||
aEntity.bEntity = bEntity;
|
||||
session.persist( aEntity );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearTestData(){
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from AEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from BEntity" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void assertIsEnhancedProxy(Object entity) {
|
||||
assertTrue( PersistentAttributeInterceptable.class.isInstance( entity ) );
|
||||
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) entity;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
assertTrue( EnhancementAsProxyLazinessInterceptor.class.isInstance( interceptor ) );
|
||||
}
|
||||
|
||||
@Entity(name = "AEntity")
|
||||
public static class AEntity {
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
private BEntity bEntity;
|
||||
}
|
||||
|
||||
@Entity(name = "BEntity")
|
||||
public static class BEntity {
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
private String description;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@SuppressWarnings({"unused", "WeakerAccess","ResultOfMethodCallIgnored"})
|
||||
@TestForIssue( jiraKey = "HHH-11147" )
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
@EnhancementOptions( lazyLoading = true )
|
||||
public class MergeProxyTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11147")
|
||||
public void testMergeDetachUninitializedProxy() {
|
||||
final Activity activity = fromTransaction(
|
||||
session -> session.get( Activity.class, 0 )
|
||||
);
|
||||
|
||||
assertThat( Hibernate.isInitialized( activity ), is( true ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( activity, "instruction" ), is( true ) );
|
||||
final Instruction instruction = activity.getInstruction();
|
||||
assertThat( Hibernate.isInitialized( instruction ), is( false ) );
|
||||
assertThat( instruction, instanceOf( PersistentAttributeInterceptable.class ) );
|
||||
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) instruction ).$$_hibernate_getInterceptor();
|
||||
assertThat( interceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
|
||||
|
||||
activity.setNbr( "2" );
|
||||
|
||||
final Activity mergedActivity = fromTransaction(
|
||||
session -> (Activity) session.merge( activity )
|
||||
);
|
||||
|
||||
assertTrue( Hibernate.isInitialized( mergedActivity ) );
|
||||
assertThat( activity, not( CoreMatchers.sameInstance( mergedActivity ) ) );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Instruction persistentInstruction = session.get( Instruction.class, 0 );
|
||||
assertThat( persistentInstruction.getSummary(), is( "Instruction #0" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11147")
|
||||
public void testMergeDetachInitializedProxy() {
|
||||
final Activity activityProxy = fromTransaction(
|
||||
session -> {
|
||||
final Activity activity = session.load( Activity.class, 0 );
|
||||
assertFalse( Hibernate.isInitialized( activity) );
|
||||
Hibernate.initialize( activity );
|
||||
return activity;
|
||||
}
|
||||
);
|
||||
|
||||
assertThat( activityProxy, not( instanceOf( HibernateProxy.class ) ) );
|
||||
assertThat( Hibernate.isInitialized( activityProxy), is( true ) );
|
||||
|
||||
{
|
||||
assertThat( Hibernate.isPropertyInitialized( activityProxy, "instruction" ), is( true ) );
|
||||
final Instruction instruction = activityProxy.getInstruction();
|
||||
assertThat( instruction, instanceOf( PersistentAttributeInterceptable.class ) );
|
||||
final PersistentAttributeInterceptor instructionInterceptor = ( (PersistentAttributeInterceptable) instruction ).$$_hibernate_getInterceptor();
|
||||
assertThat( instructionInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
|
||||
}
|
||||
|
||||
{
|
||||
assertThat( Hibernate.isPropertyInitialized( activityProxy, "webApplication" ), is( true ) );
|
||||
assertThat( activityProxy.getWebApplication(), nullValue() );
|
||||
}
|
||||
|
||||
activityProxy.setNbr( "2" );
|
||||
|
||||
final Activity mergedActivity = fromTransaction(
|
||||
session -> (Activity) session.merge( activityProxy )
|
||||
);
|
||||
|
||||
assertTrue( Hibernate.isInitialized( mergedActivity ) );
|
||||
assertThat( activityProxy, not( CoreMatchers.sameInstance( mergedActivity ) ) );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Instruction instruction = session.get( Instruction.class, 0 );
|
||||
assertThat( instruction.getSummary(), is( "Instruction #0" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11147")
|
||||
public void testMergeDetachInitializedByAccessProxy() {
|
||||
final Activity activityProxy = fromTransaction(
|
||||
session -> {
|
||||
final Activity activity = session.load( Activity.class, 0 );
|
||||
assertFalse( Hibernate.isInitialized( activity) );
|
||||
activity.getDescription();
|
||||
return activity;
|
||||
}
|
||||
);
|
||||
|
||||
assertThat( activityProxy, not( instanceOf( HibernateProxy.class ) ) );
|
||||
assertThat( Hibernate.isInitialized( activityProxy), is( true ) );
|
||||
|
||||
{
|
||||
assertThat( Hibernate.isPropertyInitialized( activityProxy, "instruction" ), is( true ) );
|
||||
final Instruction instruction = activityProxy.getInstruction();
|
||||
assertThat( instruction, instanceOf( PersistentAttributeInterceptable.class ) );
|
||||
final PersistentAttributeInterceptor instructionInterceptor = ( (PersistentAttributeInterceptable) instruction ).$$_hibernate_getInterceptor();
|
||||
assertThat( instructionInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
|
||||
}
|
||||
|
||||
{
|
||||
assertThat( Hibernate.isPropertyInitialized( activityProxy, "webApplication" ), is( true ) );
|
||||
assertThat( activityProxy.getWebApplication(), nullValue() );
|
||||
}
|
||||
|
||||
activityProxy.setNbr( "2" );
|
||||
|
||||
final Activity mergedActivity = fromTransaction(
|
||||
session -> (Activity) session.merge( activityProxy )
|
||||
);
|
||||
|
||||
assertTrue( Hibernate.isInitialized( mergedActivity ) );
|
||||
assertThat( activityProxy, not( CoreMatchers.sameInstance( mergedActivity ) ) );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Instruction instruction = session.get( Instruction.class, 0 );
|
||||
assertThat( instruction.getSummary(), is( "Instruction #0" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( Activity.class );
|
||||
sources.addAnnotatedClass( Instruction.class );
|
||||
sources.addAnnotatedClass( WebApplication.class );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
// create a slew of Activity objects, some with Instruction reference
|
||||
// some without.
|
||||
|
||||
for ( int i = 0; i < 30; i++ ) {
|
||||
final Activity activity = new Activity( i, "Activity #" + i, null );
|
||||
if ( i % 2 == 0 ) {
|
||||
final Instruction instr = new Instruction( i, "Instruction #" + i );
|
||||
activity.setInstruction( instr );
|
||||
session.save( instr );
|
||||
}
|
||||
else {
|
||||
final WebApplication webApplication = new WebApplication( i, "http://" + i + ".com" );
|
||||
activity.setWebApplication( webApplication );
|
||||
webApplication.getActivities().add( activity );
|
||||
session.save( webApplication );
|
||||
}
|
||||
|
||||
session.save( activity );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from Activity" ).executeUpdate();
|
||||
session.createQuery( "delete from Instruction" ).executeUpdate();
|
||||
session.createQuery( "delete from WebApplication" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
@MappedSuperclass
|
||||
public abstract class ModelEntity {
|
||||
|
||||
@Id
|
||||
@Column(name="Oid")
|
||||
private Long Oid = null;
|
||||
|
||||
@Basic
|
||||
@Column(name="CreatedAt")
|
||||
private Timestamp CreatedAt = null;
|
||||
|
||||
@Basic
|
||||
@Column(name="CreatedBy")
|
||||
private String CreatedBy = null;
|
||||
|
||||
@Basic
|
||||
@Column(name="VersionNr")
|
||||
private short VersionNr = 0;
|
||||
|
||||
public short getVersionNr() {
|
||||
return VersionNr;
|
||||
}
|
||||
|
||||
public void setVersionNr(short versionNr) {
|
||||
this.VersionNr = versionNr;
|
||||
}
|
||||
|
||||
public Long getOid() {
|
||||
return Oid;
|
||||
}
|
||||
|
||||
public void setOid(Long oid) {
|
||||
this.Oid = oid;
|
||||
}
|
||||
|
||||
public Timestamp getCreatedAt() {
|
||||
return CreatedAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Timestamp createdAt) {
|
||||
this.CreatedAt = createdAt;
|
||||
}
|
||||
|
||||
public String getCreatedBy() {
|
||||
return CreatedBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(String createdBy) {
|
||||
this.CreatedBy = createdBy;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
|
||||
@Entity(name="MoreSpecializedKey")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
public class MoreSpecializedKey extends SpecializedKey implements Serializable {
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "Order")
|
||||
@Table(name = "`order`")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
public class Order {
|
||||
private Integer oid;
|
||||
|
||||
private String theText;
|
||||
|
||||
private Customer customer;
|
||||
private OrderSupplemental supplemental;
|
||||
private OrderSupplemental2 supplemental2;
|
||||
|
||||
private Set<Payment> payments = new HashSet<Payment>();
|
||||
|
||||
public Order() {
|
||||
}
|
||||
|
||||
public Order(Integer oid, String theText, Customer customer) {
|
||||
this.oid = oid;
|
||||
this.theText = theText;
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column(name = "oid")
|
||||
public Integer getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(Integer oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public String getTheText() {
|
||||
return theText;
|
||||
}
|
||||
|
||||
public void setTheText(String theText) {
|
||||
this.theText = theText;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn
|
||||
// @LazyToOne( LazyToOneOption.NO_PROXY )
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
public Set<Payment> getPayments() {
|
||||
return payments;
|
||||
}
|
||||
|
||||
public void setPayments(Set<Payment> payments) {
|
||||
this.payments = payments;
|
||||
}
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "supp_info_id")
|
||||
public OrderSupplemental getSupplemental() {
|
||||
return supplemental;
|
||||
}
|
||||
|
||||
public void setSupplemental(OrderSupplemental supplemental) {
|
||||
this.supplemental = supplemental;
|
||||
}
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY, mappedBy = "order")
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
public OrderSupplemental2 getSupplemental2() {
|
||||
return supplemental2;
|
||||
}
|
||||
|
||||
public void setSupplemental2(OrderSupplemental2 supplemental2) {
|
||||
this.supplemental2 = supplemental2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "OrderSupplemental")
|
||||
@Table(name = "order_supp")
|
||||
public class OrderSupplemental {
|
||||
private Integer oid;
|
||||
private Integer receivablesId;
|
||||
|
||||
public OrderSupplemental() {
|
||||
}
|
||||
|
||||
public OrderSupplemental(Integer oid, Integer receivablesId) {
|
||||
this.oid = oid;
|
||||
this.receivablesId = receivablesId;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column(name = "oid")
|
||||
public Integer getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(Integer oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public Integer getReceivablesId() {
|
||||
return receivablesId;
|
||||
}
|
||||
|
||||
public void setReceivablesId(Integer receivablesId) {
|
||||
this.receivablesId = receivablesId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MapsId;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "OrderSupplemental2")
|
||||
@Table(name = "order_supp2")
|
||||
public class OrderSupplemental2 {
|
||||
private Integer oid;
|
||||
private Integer receivablesId;
|
||||
|
||||
private Order order;
|
||||
|
||||
public OrderSupplemental2() {
|
||||
}
|
||||
|
||||
public OrderSupplemental2(Integer oid, Integer receivablesId) {
|
||||
this.oid = oid;
|
||||
this.receivablesId = receivablesId;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column(name = "oid")
|
||||
public Integer getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(Integer oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public Integer getReceivablesId() {
|
||||
return receivablesId;
|
||||
}
|
||||
|
||||
public void setReceivablesId(Integer receivablesId) {
|
||||
this.receivablesId = receivablesId;
|
||||
}
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@MapsId
|
||||
public Order getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "Payment")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
public abstract class Payment {
|
||||
private Integer oid;
|
||||
private Float amount;
|
||||
|
||||
public Payment() {
|
||||
}
|
||||
|
||||
public Payment(Integer oid, Float amount) {
|
||||
this.oid = oid;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column(name = "oid")
|
||||
public Integer getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(Integer oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public Float getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(Float amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.sql.Blob;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ProxyDeletionTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetAndDeleteEEntity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
EEntity entity = session.get( EEntity.class, 17L );
|
||||
session.delete( entity );
|
||||
session.delete( entity.getD() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( AEntity.class );
|
||||
sources.addAnnotatedClass( BEntity.class );
|
||||
sources.addAnnotatedClass( CEntity.class );
|
||||
sources.addAnnotatedClass( DEntity.class );
|
||||
sources.addAnnotatedClass( EEntity.class );
|
||||
sources.addAnnotatedClass( GEntity.class );
|
||||
|
||||
sources.addAnnotatedClass( Activity.class );
|
||||
sources.addAnnotatedClass( Instruction.class );
|
||||
sources.addAnnotatedClass( WebApplication.class );
|
||||
|
||||
sources.addAnnotatedClass( SpecializedKey.class );
|
||||
sources.addAnnotatedClass( MoreSpecializedKey.class );
|
||||
sources.addAnnotatedClass( RoleEntity.class );
|
||||
sources.addAnnotatedClass( AbstractKey.class );
|
||||
sources.addAnnotatedClass( GenericKey.class );
|
||||
sources.addAnnotatedClass( SpecializedEntity.class );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
DEntity d = new DEntity();
|
||||
d.setD( "bla" );
|
||||
d.setOid( 1 );
|
||||
|
||||
byte[] lBytes = "agdfagdfagfgafgsfdgasfdgfgasdfgadsfgasfdgasfdgasdasfdg".getBytes();
|
||||
Blob lBlob = Hibernate.getLobCreator( session ).createBlob( lBytes );
|
||||
d.setBlob( lBlob );
|
||||
|
||||
BEntity b1 = new BEntity();
|
||||
b1.setOid( 1 );
|
||||
b1.setB1( 34 );
|
||||
b1.setB2( "huhu" );
|
||||
|
||||
BEntity b2 = new BEntity();
|
||||
b2.setOid( 2 );
|
||||
b2.setB1( 37 );
|
||||
b2.setB2( "haha" );
|
||||
|
||||
Set<BEntity> lBs = new HashSet<>();
|
||||
lBs.add( b1 );
|
||||
lBs.add( b2 );
|
||||
d.setBs( lBs );
|
||||
|
||||
AEntity a = new AEntity();
|
||||
a.setOid( 1 );
|
||||
a.setA( "hihi" );
|
||||
d.setA( a );
|
||||
|
||||
EEntity e = new EEntity();
|
||||
e.setOid( 17 );
|
||||
e.setE1( "Balu" );
|
||||
e.setE2( "Bär" );
|
||||
|
||||
e.setD( d );
|
||||
d.setE( e );
|
||||
|
||||
CEntity c = new CEntity();
|
||||
c.setOid( 1 );
|
||||
c.setC1( "ast" );
|
||||
c.setC2( "qwert" );
|
||||
c.setC3( "yxcv" );
|
||||
d.setC( c );
|
||||
|
||||
GEntity g = new GEntity();
|
||||
g.setOid( 1 );
|
||||
g.getdEntities().add( d );
|
||||
d.setG( g );
|
||||
|
||||
|
||||
session.save( b1 );
|
||||
session.save( b2 );
|
||||
session.save( a );
|
||||
session.save( c );
|
||||
session.save( g );
|
||||
session.save( d );
|
||||
session.save( e );
|
||||
|
||||
|
||||
// create a slew of Activity objects, some with Instruction reference
|
||||
// some without.
|
||||
|
||||
for ( int i = 0; i < 30; i++ ) {
|
||||
final Activity activity = new Activity( i, "Activity #" + i, null );
|
||||
if ( i % 2 == 0 ) {
|
||||
final Instruction instr = new Instruction( i, "Instruction #" + i );
|
||||
activity.setInstruction( instr );
|
||||
session.save( instr );
|
||||
}
|
||||
else {
|
||||
final WebApplication webApplication = new WebApplication( i, "http://" + i + ".com" );
|
||||
activity.setWebApplication( webApplication );
|
||||
webApplication.getActivities().add( activity );
|
||||
session.save( webApplication );
|
||||
}
|
||||
|
||||
session.save( activity );
|
||||
}
|
||||
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
roleEntity.setOid( 1L );
|
||||
|
||||
SpecializedKey specializedKey = new SpecializedKey();
|
||||
specializedKey.setOid(1L);
|
||||
|
||||
MoreSpecializedKey moreSpecializedKey = new MoreSpecializedKey();
|
||||
moreSpecializedKey.setOid( 3L );
|
||||
|
||||
SpecializedEntity specializedEntity = new SpecializedEntity();
|
||||
specializedEntity.setId( 2L );
|
||||
specializedKey.addSpecializedEntity( specializedEntity );
|
||||
specializedEntity.setSpecializedKey( specializedKey);
|
||||
|
||||
specializedKey.addRole( roleEntity );
|
||||
roleEntity.setKey( specializedKey );
|
||||
roleEntity.setSpecializedKey( moreSpecializedKey );
|
||||
moreSpecializedKey.addRole( roleEntity );
|
||||
session.save( specializedEntity );
|
||||
session.save( roleEntity );
|
||||
session.save( specializedKey );
|
||||
session.save( moreSpecializedKey );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from E" ).executeUpdate();
|
||||
session.createQuery( "delete from D" ).executeUpdate();
|
||||
session.createQuery( "delete from C" ).executeUpdate();
|
||||
session.createQuery( "delete from B" ).executeUpdate();
|
||||
session.createQuery( "delete from A" ).executeUpdate();
|
||||
session.createQuery( "delete from G" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from Activity" ).executeUpdate();
|
||||
session.createQuery( "delete from Instruction" ).executeUpdate();
|
||||
session.createQuery( "delete from WebApplication" ).executeUpdate();
|
||||
|
||||
session.createQuery( "delete from SpecializedEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from RoleEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from MoreSpecializedKey" ).executeUpdate();
|
||||
session.createQuery( "delete from SpecializedKey" ).executeUpdate();
|
||||
session.createQuery( "delete from GenericKey" ).executeUpdate();
|
||||
session.createQuery( "delete from AbstractKey" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class BaseEntity {
|
||||
@Id
|
||||
private long oid;
|
||||
private short version;
|
||||
|
||||
public long getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(long oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public short getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(short version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "A")
|
||||
@Table(name = "A")
|
||||
public static class AEntity extends BaseEntity {
|
||||
@Column(name = "A")
|
||||
private String a;
|
||||
|
||||
public String getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(String a) {
|
||||
this.a = a;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "B")
|
||||
@Table(name = "B")
|
||||
public static class BEntity extends BaseEntity {
|
||||
private Integer b1;
|
||||
private String b2;
|
||||
|
||||
public Integer getB1() {
|
||||
return b1;
|
||||
}
|
||||
|
||||
public void setB1(Integer b1) {
|
||||
this.b1 = b1;
|
||||
}
|
||||
|
||||
public String getB2() {
|
||||
return b2;
|
||||
}
|
||||
|
||||
public void setB2(String b2) {
|
||||
this.b2 = b2;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "C")
|
||||
@Table(name = "C")
|
||||
public static class CEntity extends BaseEntity {
|
||||
private String c1;
|
||||
private String c2;
|
||||
private String c3;
|
||||
private Long c4;
|
||||
|
||||
public String getC1() {
|
||||
return c1;
|
||||
}
|
||||
|
||||
public void setC1(String c1) {
|
||||
this.c1 = c1;
|
||||
}
|
||||
|
||||
public String getC2() {
|
||||
return c2;
|
||||
}
|
||||
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
public void setC2(String c2) {
|
||||
this.c2 = c2;
|
||||
}
|
||||
|
||||
public String getC3() {
|
||||
return c3;
|
||||
}
|
||||
|
||||
public void setC3(String c3) {
|
||||
this.c3 = c3;
|
||||
}
|
||||
|
||||
public Long getC4() {
|
||||
return c4;
|
||||
}
|
||||
|
||||
public void setC4(Long c4) {
|
||||
this.c4 = c4;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "D")
|
||||
@Table(name = "D")
|
||||
public static class DEntity extends BaseEntity {
|
||||
private String d;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
public AEntity a;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
public CEntity c;
|
||||
|
||||
@OneToMany(targetEntity = BEntity.class)
|
||||
public Set<BEntity> bs;
|
||||
|
||||
@OneToOne(mappedBy = "d", fetch = FetchType.LAZY)
|
||||
private EEntity e;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn()
|
||||
@LazyGroup("g")
|
||||
public GEntity g;
|
||||
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
@LazyGroup("blob")
|
||||
private Blob blob;
|
||||
|
||||
public String getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
public void setD(String d) {
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
|
||||
public AEntity getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(AEntity a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Set<BEntity> getBs() {
|
||||
return bs;
|
||||
}
|
||||
|
||||
public void setBs(Set<BEntity> bs) {
|
||||
this.bs = bs;
|
||||
}
|
||||
|
||||
public CEntity getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
public void setC(CEntity c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public Blob getBlob() {
|
||||
return blob;
|
||||
}
|
||||
|
||||
public void setBlob(Blob blob) {
|
||||
this.blob = blob;
|
||||
}
|
||||
|
||||
public EEntity getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
public void setE(EEntity e) {
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
public GEntity getG() {
|
||||
return g;
|
||||
}
|
||||
|
||||
public void setG(GEntity g) {
|
||||
this.g = g;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "E")
|
||||
@Table(name = "E")
|
||||
public static class EEntity extends BaseEntity {
|
||||
private String e1;
|
||||
private String e2;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
private DEntity d;
|
||||
|
||||
public String getE1() {
|
||||
return e1;
|
||||
}
|
||||
|
||||
public void setE1(String e1) {
|
||||
this.e1 = e1;
|
||||
}
|
||||
|
||||
public String getE2() {
|
||||
return e2;
|
||||
}
|
||||
|
||||
public void setE2(String e2) {
|
||||
this.e2 = e2;
|
||||
}
|
||||
|
||||
public DEntity getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
public void setD(DEntity d) {
|
||||
this.d = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "G")
|
||||
@Table(name = "G")
|
||||
public static class GEntity extends BaseEntity {
|
||||
|
||||
@OneToMany(mappedBy = "g")
|
||||
public Set<DEntity> dEntities = new HashSet<>();
|
||||
|
||||
public Set<DEntity> getdEntities() {
|
||||
return dEntities;
|
||||
}
|
||||
|
||||
public void setdEntities(Set<DEntity> dEntities) {
|
||||
this.dEntities = dEntities;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
|
||||
@Entity(name = "RoleEntity")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
@Table(name = "PP_DCRolleKey")
|
||||
public class RoleEntity extends ModelEntity implements Serializable {
|
||||
|
||||
@Basic
|
||||
Short value;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.PROXY)
|
||||
@LazyGroup("Key")
|
||||
@JoinColumn
|
||||
protected AbstractKey key = null;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.PROXY)
|
||||
@LazyGroup("Key")
|
||||
@JoinColumn
|
||||
protected SpecializedKey specializedKey = null;
|
||||
|
||||
public Short getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setvalue(Short value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public AbstractKey getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(AbstractKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public SpecializedKey getSpecializedKey() {
|
||||
return specializedKey;
|
||||
}
|
||||
|
||||
public void setSpecializedKey(SpecializedKey specializedKey) {
|
||||
this.specializedKey = specializedKey;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-11147")
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@EnhancementOptions(lazyLoading = true)
|
||||
public class SimpleUpdateTestWithLazyLoading extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
|
||||
ssrb.applySetting( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
private static final int CHILDREN_SIZE = 10;
|
||||
private Long lastChildID;
|
||||
|
||||
@Override
|
||||
public Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { Parent.class, Child.class, Person.class };
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Parent parent = new Parent();
|
||||
for ( int i = 0; i < CHILDREN_SIZE; i++ ) {
|
||||
Child child = new Child();
|
||||
// Association management should kick in here
|
||||
child.parent = parent;
|
||||
|
||||
Person relative = new Person();
|
||||
relative.setName( "Luigi" );
|
||||
child.addRelative( relative );
|
||||
|
||||
s.persist( relative );
|
||||
|
||||
s.persist( child );
|
||||
lastChildID = child.id;
|
||||
}
|
||||
s.persist( parent );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
s.createQuery( "delete from Child" ).executeUpdate();
|
||||
s.createQuery( "delete from Parent" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSimpleField() {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
final String updatedName = "Barrabas_";
|
||||
|
||||
final EntityPersister childPersister = sessionFactory().getMetamodel().entityPersister( Child.class.getName() );
|
||||
|
||||
final int relativesAttributeIndex = childPersister.getEntityMetamodel().getPropertyIndex( "relatives" );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
stats.clear();
|
||||
Child loadedChild = session.load( Child.class, lastChildID );
|
||||
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) loadedChild;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
MatcherAssert.assertThat( interceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
|
||||
final EnhancementAsProxyLazinessInterceptor proxyInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
|
||||
|
||||
loadedChild.setName( updatedName );
|
||||
|
||||
// ^ should have triggered "base fetch group" initialization which would mean a SQL select
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
|
||||
// check that the `#setName` "persisted"
|
||||
assertThat( loadedChild.getName(), is( updatedName ) );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
|
||||
final EntityEntry entry = session.getPersistenceContext().getEntry( loadedChild );
|
||||
assertThat(
|
||||
entry.getLoadedState()[ relativesAttributeIndex ],
|
||||
is( LazyPropertyInitializer.UNFETCHED_PROPERTY )
|
||||
);
|
||||
|
||||
// force a flush - the relatives collection should still be UNFETCHED_PROPERTY afterwards
|
||||
session.flush();
|
||||
|
||||
final EntityEntry updatedEntry = session.getPersistenceContext().getEntry( loadedChild );
|
||||
assertThat( updatedEntry, sameInstance( entry ) );
|
||||
|
||||
assertThat(
|
||||
entry.getLoadedState()[ relativesAttributeIndex ],
|
||||
is( LazyPropertyInitializer.UNFETCHED_PROPERTY )
|
||||
);
|
||||
|
||||
session.getEventListenerManager();
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Child loadedChild = session.load( Child.class, lastChildID );
|
||||
assertThat( loadedChild.getName(), is( updatedName ) );
|
||||
|
||||
final EntityEntry entry = session.getPersistenceContext().getEntry( loadedChild );
|
||||
assertThat(
|
||||
entry.getLoadedState()[ relativesAttributeIndex ],
|
||||
is( LazyPropertyInitializer.UNFETCHED_PROPERTY )
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAssociation() {
|
||||
String updatedName = "Barrabas_";
|
||||
String parentName = "Yodit";
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
|
||||
loadedChild.setName( updatedName );
|
||||
|
||||
Parent parent = new Parent();
|
||||
parent.setName( parentName );
|
||||
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
loadedChild.setParent( parent );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
assertThat( loadedChild.getParent().getName(), is( parentName ) );
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
s.save( parent );
|
||||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
assertThat( loadedChild.getName(), is( updatedName ) );
|
||||
assertThat( loadedChild.getParent().getName(), is( parentName ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCollection() {
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
|
||||
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
Person relative = new Person();
|
||||
relative.setName( "Luis" );
|
||||
loadedChild.addRelative( relative );
|
||||
assertEquals( 2, stats.getPrepareStatementCount() );
|
||||
s.persist( relative );
|
||||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
assertThat( loadedChild.getRelatives().size(), is( 2 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Parent")
|
||||
@Table(name = "PARENT")
|
||||
private static class Parent {
|
||||
|
||||
String name;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
Long id;
|
||||
|
||||
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
List<Child> children;
|
||||
|
||||
void setChildren(List<Child> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Table(name = "Person")
|
||||
private static class Person {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Child")
|
||||
@Table(name = "CHILD")
|
||||
private static class Child {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
Long id;
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
Parent parent;
|
||||
|
||||
@OneToMany
|
||||
List<Person> relatives;
|
||||
|
||||
String name;
|
||||
|
||||
Child() {
|
||||
}
|
||||
|
||||
Child(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Parent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Parent parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public List<Person> getRelatives() {
|
||||
return relatives;
|
||||
}
|
||||
|
||||
public void setRelatives(List<Person> relatives) {
|
||||
this.relatives = relatives;
|
||||
}
|
||||
|
||||
public void addRelative(Person person) {
|
||||
if ( this.relatives == null ) {
|
||||
this.relatives = new ArrayList<>();
|
||||
}
|
||||
this.relatives.add( person );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-11147")
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@EnhancementOptions( lazyLoading = true, inlineDirtyChecking = true )
|
||||
public class SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
|
||||
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
|
||||
ssrb.applySetting( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
private static final int CHILDREN_SIZE = 10;
|
||||
private Long lastChildID;
|
||||
|
||||
@Override
|
||||
public Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { Parent.class, Child.class, Person.class };
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Parent parent = new Parent();
|
||||
for ( int i = 0; i < CHILDREN_SIZE; i++ ) {
|
||||
Child child = new Child();
|
||||
// Association management should kick in here
|
||||
child.parent = parent;
|
||||
|
||||
Person relative = new Person();
|
||||
relative.setName( "Luigi" );
|
||||
child.addRelative( relative );
|
||||
|
||||
s.persist( relative );
|
||||
|
||||
s.persist( child );
|
||||
lastChildID = child.id;
|
||||
}
|
||||
s.persist( parent );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
s.createQuery( "delete from Child" ).executeUpdate();
|
||||
s.createQuery( "delete from Parent" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSimpleField() {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
String updatedName = "Barrabas_";
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
stats.clear();
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) loadedChild;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
MatcherAssert.assertThat( interceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
|
||||
|
||||
loadedChild.setName( updatedName );
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
assertThat( loadedChild.getName(), is( updatedName ) );
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
} );
|
||||
|
||||
// the UPDATE
|
||||
assertEquals( 1, stats.getPrepareStatementCount() );
|
||||
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
assertThat( loadedChild.getName(), is( updatedName ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAssociation() {
|
||||
String updatedName = "Barrabas_";
|
||||
String parentName = "Yodit";
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
|
||||
loadedChild.setName( updatedName );
|
||||
|
||||
Parent parent = new Parent();
|
||||
parent.setName( parentName );
|
||||
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
loadedChild.setParent( parent );
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
assertThat( loadedChild.getParent().getName(), is( parentName ) );
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
s.save( parent );
|
||||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
assertThat( loadedChild.getName(), is( updatedName ) );
|
||||
assertThat( loadedChild.getParent().getName(), is( parentName ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCollection() {
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
|
||||
assertEquals( 0, stats.getPrepareStatementCount() );
|
||||
Person relative = new Person();
|
||||
relative.setName( "Luis" );
|
||||
loadedChild.addRelative( relative );
|
||||
|
||||
// forces SELECT related to uninitialized Child, and then forces
|
||||
// SELECT of Child#relatives collection for the add
|
||||
assertEquals( 2, stats.getPrepareStatementCount() );
|
||||
s.persist( relative );
|
||||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Child loadedChild = s.load( Child.class, lastChildID );
|
||||
assertThat( loadedChild.getRelatives().size(), is( 2 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Parent")
|
||||
@Table(name = "PARENT")
|
||||
private static class Parent {
|
||||
|
||||
String name;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
Long id;
|
||||
|
||||
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
List<Child> children;
|
||||
|
||||
void setChildren(List<Child> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Table(name = "Person")
|
||||
private static class Person {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Child")
|
||||
@Table(name = "CHILD")
|
||||
private static class Child {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
Long id;
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
Parent parent;
|
||||
|
||||
@OneToMany
|
||||
List<Person> relatives;
|
||||
|
||||
String name;
|
||||
|
||||
Child() {
|
||||
}
|
||||
|
||||
Child(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Parent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Parent parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public List<Person> getRelatives() {
|
||||
return relatives;
|
||||
}
|
||||
|
||||
public void setRelatives(List<Person> relatives) {
|
||||
this.relatives = relatives;
|
||||
}
|
||||
|
||||
public void addRelative(Person person) {
|
||||
if ( this.relatives == null ) {
|
||||
this.relatives = new ArrayList<>();
|
||||
}
|
||||
this.relatives.add( person );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
|
||||
|
||||
@Entity(name="SpecializedEntity")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
@Table(name="PP_PartnerZusatzKuerzelZR")
|
||||
public class SpecializedEntity implements Serializable {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Column(name="Value")
|
||||
String value;
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.PROXY)
|
||||
@LazyGroup("SpecializedKey")
|
||||
@JoinColumn
|
||||
protected SpecializedKey specializedKey = null;
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public SpecializedKey getSpecializedKey() {
|
||||
return specializedKey;
|
||||
}
|
||||
|
||||
public void setSpecializedKey(SpecializedKey specializedKey) {
|
||||
this.specializedKey = specializedKey;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
|
||||
@Entity(name="SpecializedKey")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
@Table(name="PP_PartnerDCKey")
|
||||
public class SpecializedKey extends GenericKey implements Serializable
|
||||
{
|
||||
|
||||
@OneToMany(targetEntity= SpecializedEntity.class, mappedBy="specializedKey", fetch=FetchType.LAZY)
|
||||
// @LazyCollection( LazyCollectionOption.EXTRA )
|
||||
protected Set<SpecializedEntity> specializedEntities = new LinkedHashSet();
|
||||
|
||||
public Set<SpecializedEntity> getSpecializedEntities() {
|
||||
return specializedEntities;
|
||||
}
|
||||
|
||||
public void setSpecializedEntities(Set<SpecializedEntity> specializedEntities) {
|
||||
this. specializedEntities = specializedEntities;
|
||||
}
|
||||
|
||||
public void addSpecializedEntity(SpecializedEntity pPartnerZusatzkuerzelZR) {
|
||||
this.specializedEntities.add( pPartnerZusatzkuerzelZR);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.LazyGroup;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
@Table( name="web_app" )
|
||||
public class WebApplication extends BaseEntity {
|
||||
private String name;
|
||||
private String siteUrl;
|
||||
|
||||
private Set<Activity> activities = new HashSet<>();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public WebApplication() {
|
||||
}
|
||||
|
||||
public WebApplication(Integer id, String siteUrl) {
|
||||
super( id );
|
||||
this.siteUrl = siteUrl;
|
||||
}
|
||||
|
||||
@NaturalId
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Basic( fetch = FetchType.LAZY )
|
||||
public String getSiteUrl() {
|
||||
return siteUrl;
|
||||
}
|
||||
|
||||
public void setSiteUrl(String siteUrl) {
|
||||
this.siteUrl = siteUrl;
|
||||
}
|
||||
|
||||
@OneToMany(mappedBy="webApplication", fetch= FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@LazyGroup("app_activity_group")
|
||||
// @CollectionType(type="baseutil.technology.hibernate.IskvLinkedSetCollectionType")
|
||||
public Set<Activity> getActivities() {
|
||||
return activities;
|
||||
}
|
||||
|
||||
public void setActivities(Set<Activity> activities) {
|
||||
this.activities = activities;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for lazy-enhancement-as-proxy support
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.lazy.proxy;
|
|
@ -71,7 +71,7 @@ public class CustomPersister implements EntityPersister {
|
|||
NaturalIdDataAccess naturalIdRegionAccessStrategy,
|
||||
PersisterCreationContext creationContext) {
|
||||
this.factory = creationContext.getSessionFactory();
|
||||
this.entityMetamodel = new EntityMetamodel( model, this, factory );
|
||||
this.entityMetamodel = new EntityMetamodel( model, this, creationContext );
|
||||
}
|
||||
|
||||
public boolean hasLazyProperties() {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.test.stateless.fetching;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
public class Producer {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany( mappedBy = "producer", fetch = FetchType.LAZY )
|
||||
private Set<Product> products = new HashSet<>();
|
||||
|
||||
public Producer() {
|
||||
}
|
||||
|
||||
public Producer(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Product> getProducts() {
|
||||
return products;
|
||||
}
|
||||
|
||||
public void setProducts(Set<Product> products) {
|
||||
this.products = products;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.test.stateless.fetching;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
public class Product {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String sku;
|
||||
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
private Vendor vendor;
|
||||
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
private Producer producer;
|
||||
|
||||
public Product() {
|
||||
}
|
||||
|
||||
public Product(Integer id, String sku, Vendor vendor, Producer producer) {
|
||||
this.id = id;
|
||||
this.sku = sku;
|
||||
this.vendor = vendor;
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSku() {
|
||||
return sku;
|
||||
}
|
||||
|
||||
public void setSku(String sku) {
|
||||
this.sku = sku;
|
||||
}
|
||||
|
||||
public Vendor getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
public void setVendor(Vendor vendor) {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
public Producer getProducer() {
|
||||
return producer;
|
||||
}
|
||||
|
||||
public void setProducer(Producer producer) {
|
||||
this.producer = producer;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,9 @@ import java.util.Date;
|
|||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.Query;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StatelessSession;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
|
@ -21,8 +24,6 @@ import org.hibernate.internal.util.StringHelper;
|
|||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -36,6 +37,11 @@ public class StatelessSessionFetchingTest extends BaseCoreFunctionalTestCase {
|
|||
return new String[] { "stateless/fetching/Mappings.hbm.xml" };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { Producer.class, Product.class, Vendor.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(Configuration cfg) {
|
||||
super.configure( cfg );
|
||||
|
@ -91,12 +97,161 @@ public class StatelessSessionFetchingTest extends BaseCoreFunctionalTestCase {
|
|||
cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicFetchScroll() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
Date now = new Date();
|
||||
|
||||
User me = new User( "me" );
|
||||
User you = new User( "you" );
|
||||
Resource yourClock = new Resource( "clock", you );
|
||||
Task task = new Task( me, "clean", yourClock, now ); // :)
|
||||
|
||||
s.save( me );
|
||||
s.save( you );
|
||||
s.save( yourClock );
|
||||
s.save( task );
|
||||
|
||||
User u3 = new User( "U3" );
|
||||
User u4 = new User( "U4" );
|
||||
Resource it = new Resource( "it", u4 );
|
||||
Task task2 = new Task( u3, "beat", it, now ); // :))
|
||||
|
||||
s.save( u3 );
|
||||
s.save( u4 );
|
||||
s.save( it );
|
||||
s.save( task2 );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
StatelessSession ss = sessionFactory().openStatelessSession();
|
||||
ss.beginTransaction();
|
||||
|
||||
final Query query = ss.createQuery( "from Task t join fetch t.resource join fetch t.user");
|
||||
final ScrollableResults scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY);
|
||||
while ( scrollableResults.next() ) {
|
||||
Task taskRef = (Task) scrollableResults.get( 0 );
|
||||
assertTrue( Hibernate.isInitialized( taskRef ) );
|
||||
assertTrue( Hibernate.isInitialized( taskRef.getUser() ) );
|
||||
assertTrue( Hibernate.isInitialized( taskRef.getResource() ) );
|
||||
assertFalse( Hibernate.isInitialized( taskRef.getResource().getOwner() ) );
|
||||
}
|
||||
|
||||
ss.getTransaction().commit();
|
||||
ss.close();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicFetchScrollSession() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
Date now = new Date();
|
||||
|
||||
User me = new User( "me" );
|
||||
User you = new User( "you" );
|
||||
Resource yourClock = new Resource( "clock", you );
|
||||
Task task = new Task( me, "clean", yourClock, now ); // :)
|
||||
|
||||
s.save( me );
|
||||
s.save( you );
|
||||
s.save( yourClock );
|
||||
s.save( task );
|
||||
|
||||
User u3 = new User( "U3" );
|
||||
User u4 = new User( "U4" );
|
||||
Resource it = new Resource( "it", u4 );
|
||||
Task task2 = new Task( u3, "beat", it, now ); // :))
|
||||
|
||||
s.save( u3 );
|
||||
s.save( u4 );
|
||||
s.save( it );
|
||||
s.save( task2 );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
final Query query = session.createQuery( "from Task t join fetch t.resource join fetch t.user");
|
||||
final ScrollableResults scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY );
|
||||
while ( scrollableResults.next() ) {
|
||||
Task taskRef = (Task) scrollableResults.get( 0 );
|
||||
assertTrue( Hibernate.isInitialized( taskRef ) );
|
||||
assertTrue( Hibernate.isInitialized( taskRef.getUser() ) );
|
||||
assertTrue( Hibernate.isInitialized( taskRef.getResource() ) );
|
||||
assertFalse( Hibernate.isInitialized( taskRef.getResource().getOwner() ) );
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicFetchCollectionScroll() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
Producer p1 = new Producer( 1, "Acme" );
|
||||
Producer p2 = new Producer( 2, "ABC" );
|
||||
|
||||
session.save( p1 );
|
||||
session.save( p2 );
|
||||
|
||||
Vendor v1 = new Vendor( 1, "v1" );
|
||||
Vendor v2 = new Vendor( 2, "v2" );
|
||||
|
||||
session.save( v1 );
|
||||
session.save( v2 );
|
||||
|
||||
final Product product1 = new Product(1, "123", v1, p1);
|
||||
final Product product2 = new Product(2, "456", v1, p1);
|
||||
final Product product3 = new Product(3, "789", v1, p2);
|
||||
|
||||
session.save( product1 );
|
||||
session.save( product2 );
|
||||
session.save( product3 );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
StatelessSession ss = sessionFactory().openStatelessSession();
|
||||
ss.beginTransaction();
|
||||
|
||||
final Query query = ss.createQuery( "select p from Producer p join fetch p.products" );
|
||||
final ScrollableResults scrollableResults = query.scroll(ScrollMode.FORWARD_ONLY);
|
||||
while ( scrollableResults.next() ) {
|
||||
Producer producer = (Producer) scrollableResults.get( 0 );
|
||||
assertTrue( Hibernate.isInitialized( producer ) );
|
||||
assertTrue( Hibernate.isInitialized( producer.getProducts() ) );
|
||||
|
||||
for (Product product : producer.getProducts()) {
|
||||
assertTrue( Hibernate.isInitialized( product ) );
|
||||
assertFalse( Hibernate.isInitialized( product.getVendor() ) );
|
||||
}
|
||||
}
|
||||
|
||||
ss.getTransaction().commit();
|
||||
ss.close();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete Task" ).executeUpdate();
|
||||
s.createQuery( "delete Resource" ).executeUpdate();
|
||||
s.createQuery( "delete User" ).executeUpdate();
|
||||
|
||||
s.createQuery( "delete Product" ).executeUpdate();
|
||||
s.createQuery( "delete Producer" ).executeUpdate();
|
||||
s.createQuery( "delete Vendor" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.test.stateless.fetching;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
public class Vendor {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "vendor", fetch = FetchType.LAZY )
|
||||
private Set<Product> products = new HashSet<>();
|
||||
|
||||
public Vendor() {
|
||||
}
|
||||
|
||||
public Vendor(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Product> getProducts() {
|
||||
return products;
|
||||
}
|
||||
|
||||
public void setProducts(Set<Product> products) {
|
||||
this.products = products;
|
||||
}
|
||||
}
|
|
@ -115,6 +115,7 @@ public class LobUnfetchedPropertyTest extends BaseCoreFunctionalTestCase {
|
|||
FileNClob file = s.get( FileNClob.class, id );
|
||||
assertFalse( Hibernate.isPropertyInitialized( file, "clob" ) );
|
||||
NClob nClob = file.getClob();
|
||||
assertTrue( Hibernate.isPropertyInitialized( file, "clob" ) );
|
||||
try {
|
||||
final char[] chars = new char[(int) file.getClob().length()];
|
||||
nClob.getCharacterStream().read( chars );
|
||||
|
|
|
@ -49,9 +49,9 @@ log4j.logger.org.hibernate.engine.internal.StatisticalLoggingSessionEventListene
|
|||
|
||||
log4j.logger.org.hibernate.boot.model.source.internal.hbm.ModelBinder=debug
|
||||
log4j.logger.org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry=debug
|
||||
log4j.logger.org.hibernate.action.internal.EntityAction=debug
|
||||
|
||||
log4j.logger.org.hibernate.engine.internal.Cascade=trace
|
||||
#log4j.logger.org.hibernate.action.internal.EntityAction=debug
|
||||
#log4j.logger.org.hibernate.engine.internal.Cascade=trace
|
||||
|
||||
### When entity copy merge functionality is enabled using:
|
||||
### hibernate.event.merge.entity_copy_observer=log, the following will
|
||||
|
|
|
@ -10,13 +10,20 @@ import java.io.BufferedInputStream;
|
|||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||
import org.hibernate.cfg.Environment;
|
||||
|
||||
import org.hibernate.testing.junit4.CustomRunner;
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
|
@ -46,7 +53,16 @@ public class BytecodeEnhancerRunner extends Suite {
|
|||
List<Class<?>> classList = new ArrayList<>();
|
||||
|
||||
try {
|
||||
if ( klass.isAnnotationPresent( CustomEnhancementContext.class ) ) {
|
||||
if ( klass.isAnnotationPresent( EnhancementOptions.class )
|
||||
|| klass.isAnnotationPresent( ClassEnhancementSelector.class )
|
||||
|| klass.isAnnotationPresent( ClassEnhancementSelectors.class )
|
||||
|| klass.isAnnotationPresent( PackageEnhancementSelector.class )
|
||||
|| klass.isAnnotationPresent( PackageEnhancementSelectors.class )
|
||||
|| klass.isAnnotationPresent( ImplEnhancementSelector.class )
|
||||
|| klass.isAnnotationPresent( ImplEnhancementSelectors.class ) ) {
|
||||
classList.add( buildEnhancerClassLoader( klass ).loadClass( klass.getName() ) );
|
||||
}
|
||||
else if ( klass.isAnnotationPresent( CustomEnhancementContext.class ) ) {
|
||||
for ( Class<? extends EnhancementContext> contextClass : klass.getAnnotation( CustomEnhancementContext.class ).value() ) {
|
||||
EnhancementContext enhancementContextInstance = contextClass.getConstructor().newInstance();
|
||||
classList.add( getEnhancerClassLoader( enhancementContextInstance, packageName ).loadClass( klass.getName() ) );
|
||||
|
@ -65,52 +81,166 @@ public class BytecodeEnhancerRunner extends Suite {
|
|||
|
||||
// --- //
|
||||
|
||||
private static ClassLoader getEnhancerClassLoader(EnhancementContext context, String packageName) {
|
||||
return new ClassLoader() {
|
||||
|
||||
private final String debugOutputDir = System.getProperty( "java.io.tmpdir" );
|
||||
|
||||
private final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( context );
|
||||
|
||||
@SuppressWarnings( "ResultOfMethodCallIgnored" )
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
if ( !name.startsWith( packageName ) ) {
|
||||
return getParent().loadClass( name );
|
||||
}
|
||||
Class c = findLoadedClass( name );
|
||||
if ( c != null ) {
|
||||
return c;
|
||||
private static ClassLoader buildEnhancerClassLoader(Class<?> klass) {
|
||||
final EnhancementOptions options = klass.getAnnotation( EnhancementOptions.class );
|
||||
final EnhancementContext enhancerContext;
|
||||
if ( options == null ) {
|
||||
enhancerContext = new EnhancerTestContext();
|
||||
}
|
||||
else {
|
||||
enhancerContext = new EnhancerTestContext() {
|
||||
@Override
|
||||
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
|
||||
return options.biDirectionalAssociationManagement() && super.doBiDirectionalAssociationManagement( field );
|
||||
}
|
||||
|
||||
try ( InputStream is = getResourceAsStream( name.replace( '.', '/' ) + ".class" ) ) {
|
||||
if ( is == null ) {
|
||||
throw new ClassNotFoundException( name + " not found" );
|
||||
}
|
||||
|
||||
byte[] original = new byte[is.available()];
|
||||
try ( BufferedInputStream bis = new BufferedInputStream( is ) ) {
|
||||
bis.read( original );
|
||||
}
|
||||
|
||||
byte[] enhanced = enhancer.enhance( name, original );
|
||||
if ( enhanced == null ) {
|
||||
return defineClass( name, original, 0, original.length );
|
||||
}
|
||||
|
||||
File f = new File( debugOutputDir + File.separator + name.replace( ".", File.separator ) + ".class" );
|
||||
f.getParentFile().mkdirs();
|
||||
f.createNewFile();
|
||||
try ( FileOutputStream out = new FileOutputStream( f ) ) {
|
||||
out.write( enhanced );
|
||||
}
|
||||
return defineClass( name, enhanced, 0, enhanced.length );
|
||||
@Override
|
||||
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
|
||||
return options.inlineDirtyChecking() && super.doDirtyCheckingInline( classDescriptor );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new ClassNotFoundException( name + " not found", t );
|
||||
|
||||
@Override
|
||||
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
|
||||
return options.extendedEnhancement() && super.doExtendedEnhancement( classDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||
return options.lazyLoading() && super.hasLazyLoadableAttributes( classDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLazyLoadable(UnloadedField field) {
|
||||
return options.lazyLoading() && super.isLazyLoadable( field );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
final List<EnhancementSelector> selectors = new ArrayList<>();
|
||||
selectors.add( new PackageSelector( klass.getPackage().getName() ) );
|
||||
applySelectors(
|
||||
klass,
|
||||
ClassEnhancementSelector.class,
|
||||
ClassEnhancementSelectors.class,
|
||||
selectorAnnotation -> selectors.add( new ClassSelector( selectorAnnotation.value().getName() ) )
|
||||
);
|
||||
applySelectors(
|
||||
klass,
|
||||
PackageEnhancementSelector.class,
|
||||
PackageEnhancementSelectors.class,
|
||||
selectorAnnotation -> selectors.add( new PackageSelector( selectorAnnotation.value() ) )
|
||||
);
|
||||
applySelectors(
|
||||
klass,
|
||||
ImplEnhancementSelector.class,
|
||||
ImplEnhancementSelectors.class,
|
||||
selectorAnnotation -> {
|
||||
try {
|
||||
selectors.add( selectorAnnotation.impl().newInstance() );
|
||||
}
|
||||
catch ( RuntimeException re ) {
|
||||
throw re;
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return buildEnhancerClassLoader( enhancerContext, selectors );
|
||||
}
|
||||
|
||||
private static <A extends Annotation> void applySelectors(
|
||||
Class<?> klass,
|
||||
Class<A> selectorAnnotationType,
|
||||
Class<? extends Annotation> selectorsAnnotationType,
|
||||
Consumer<A> action) {
|
||||
final A selectorAnnotation = klass.getAnnotation( selectorAnnotationType );
|
||||
final Annotation selectorsAnnotation = klass.getAnnotation( selectorsAnnotationType );
|
||||
|
||||
if ( selectorAnnotation != null ) {
|
||||
action.accept( selectorAnnotation );
|
||||
}
|
||||
else if ( selectorsAnnotation != null ) {
|
||||
try {
|
||||
final Method valuesMethod = selectorsAnnotationType.getDeclaredMethods()[0];
|
||||
//noinspection unchecked
|
||||
final A[] selectorAnnotations = (A[]) valuesMethod.invoke( selectorsAnnotation );
|
||||
for ( A groupedSelectorAnnotation : selectorAnnotations ) {
|
||||
action.accept( groupedSelectorAnnotation );
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassLoader buildEnhancerClassLoader(
|
||||
EnhancementContext enhancerContext,
|
||||
List<EnhancementSelector> selectors) {
|
||||
return new EnhancingClassLoader(
|
||||
Environment.getBytecodeProvider().getEnhancer( enhancerContext ),
|
||||
selectors
|
||||
);
|
||||
}
|
||||
|
||||
private static class EnhancingClassLoader extends ClassLoader {
|
||||
private static final String debugOutputDir = System.getProperty( "java.io.tmpdir" );
|
||||
|
||||
private final Enhancer enhancer;
|
||||
private final List<EnhancementSelector> selectors;
|
||||
|
||||
public EnhancingClassLoader(Enhancer enhancer, List<EnhancementSelector> selectors) {
|
||||
this.enhancer = enhancer;
|
||||
this.selectors = selectors;
|
||||
}
|
||||
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
for ( EnhancementSelector selector : selectors ) {
|
||||
if ( selector.select( name ) ) {
|
||||
final Class c = findLoadedClass( name );
|
||||
if ( c != null ) {
|
||||
return c;
|
||||
}
|
||||
|
||||
try ( InputStream is = getResourceAsStream( name.replace( '.', '/' ) + ".class" ) ) {
|
||||
if ( is == null ) {
|
||||
throw new ClassNotFoundException( name + " not found" );
|
||||
}
|
||||
|
||||
byte[] original = new byte[is.available()];
|
||||
try ( BufferedInputStream bis = new BufferedInputStream( is ) ) {
|
||||
bis.read( original );
|
||||
}
|
||||
|
||||
byte[] enhanced = enhancer.enhance( name, original );
|
||||
if ( enhanced == null ) {
|
||||
return defineClass( name, original, 0, original.length );
|
||||
}
|
||||
|
||||
File f = new File( debugOutputDir + File.separator + name.replace( ".", File.separator ) + ".class" );
|
||||
f.getParentFile().mkdirs();
|
||||
f.createNewFile();
|
||||
try ( FileOutputStream out = new FileOutputStream( f ) ) {
|
||||
out.write( enhanced );
|
||||
}
|
||||
return defineClass( name, enhanced, 0, enhanced.length );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new ClassNotFoundException( name + " not found", t );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return getParent().loadClass( name );
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassLoader getEnhancerClassLoader(EnhancementContext context, String packageName) {
|
||||
return buildEnhancerClassLoader( context, Collections.singletonList( new PackageSelector( packageName ) ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.testing.bytecode.enhancement;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Retention( RetentionPolicy.RUNTIME)
|
||||
@Target( ElementType.TYPE)
|
||||
@Inherited
|
||||
@Repeatable( ClassEnhancementSelectors.class )
|
||||
public @interface ClassEnhancementSelector {
|
||||
Class<?> value();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue