HHH-11147 - Allow enhanced entities to be returned in a completely uninitialized state

This commit is contained in:
Steve Ebersole 2019-04-18 02:28:29 -05:00
parent e4a3586316
commit 5d518cb673
112 changed files with 10455 additions and 747 deletions

View File

@ -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 );
}
}

View File

@ -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

View File

@ -432,4 +432,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
public boolean nativeExceptionHandling51Compliance() {
return delegate.nativeExceptionHandling51Compliance();
}
@Override
public boolean isEnhancementAsProxyEnabled() {
return delegate.isEnhancementAsProxyEnabled();
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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) {
}
}
/**

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 );

View File

@ -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();
}

View File

@ -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);

View File

@ -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
*/

View File

@ -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() ) ) {

View File

@ -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 );

View File

@ -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 );
}
/**

View File

@ -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;

View File

@ -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;
}

View File

@ -341,7 +341,7 @@ public final class TwoPhaseLoad {
return true;
}
}
return null;
}

View File

@ -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

View File

@ -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) {
}
}

View File

@ -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
*/

View File

@ -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();

View File

@ -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) {

View File

@ -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 ) {

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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];

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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);
/**

View File

@ -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(),

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -46,6 +46,7 @@ public class PojoEntityInstantiator extends PojoInstantiator {
PersistentAttributeInterceptor interceptor = new LazyAttributeLoadingInterceptor(
entityMetamodel.getName(),
null,
entityMetamodel.getBytecodeEnhancementMetadata()
.getLazyAttributesMetadata()
.getLazyAttributeNames(),

View File

@ -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 ) {

View File

@ -683,7 +683,8 @@ public abstract class EntityType extends AbstractType implements AssociationType
getAssociatedEntityName(),
id,
eager,
isNullable()
isNullable(),
unwrapProxy
);
if ( proxyOrEntity instanceof HibernateProxy ) {

View File

@ -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};

View File

@ -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) {
}
}
// --- //

View File

@ -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(

View File

@ -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;
}
}
}

View File

@ -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" );

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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();
}
);
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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 );

View File

@ -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

View File

@ -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

View File

@ -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