Merge remote-tracking branch 'upstream/master' into wip/6.0

This commit is contained in:
Andrea Boriero 2021-02-24 15:02:45 +01:00
commit 005c4cea63
46 changed files with 3506 additions and 533 deletions

View File

@ -329,9 +329,9 @@ test {
jvmArgs '-XX:+StartAttachListener'
}
// Enable the experimental features of ByteBuddy with JDK 15+
// Enable the experimental features of ByteBuddy with JDK 18+
test {
if ( gradle.ext.javaVersions.test.release.asInt() >= 15 ) {
if ( gradle.ext.javaVersions.test.release.asInt() >= 18 ) {
logger.warn( "The version of Java bytecode that will be tested is not supported by Bytebuddy by default. " +
" Setting 'net.bytebuddy.experimental=true'." )
systemProperty 'net.bytebuddy.experimental', true

View File

@ -26,7 +26,7 @@ ext {
weldVersion = '3.1.5.Final'
javassistVersion = '3.27.0-GA'
byteBuddyVersion = '1.10.17'
byteBuddyVersion = '1.10.21'
agroalVersion = '1.9'

View File

@ -6,17 +6,20 @@
*/
package org.hibernate.bytecode;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public interface BytecodeLogger extends BasicLogger {
public interface BytecodeLogging {
String NAME = "org.hibernate.orm.bytecode";
Logger LOGGER = Logger.getLogger( NAME );
static String subLoggerName(String subName) {
return NAME + "." + subName;
}
boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
}

View File

@ -0,0 +1,43 @@
/*
* 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.bytecode.BytecodeLogging;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.ValidIdRange;
import static org.jboss.logging.Logger.Level.WARN;
/**
* Logging related to bytecode enhancement interceptors
*/
@MessageLogger(projectCode = "HHH")
@ValidIdRange(min = 90005901, max = 90006000)
public interface BytecodeInterceptorLogging extends BasicLogger {
String SUB_NAME = "interceptor";
String NAME = BytecodeLogging.subLoggerName(SUB_NAME);
Logger LOGGER = Logger.getLogger(NAME);
BytecodeInterceptorLogging MESSAGE_LOGGER = Logger.getMessageLogger(BytecodeInterceptorLogging.class, NAME);
boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
@LogMessage(level = WARN)
@Message(
id = 90005901,
value = "`%s#%s` was mapped with explicit lazy-group (`%s`). Hibernate will ignore the lazy-group - this is generally " +
"not a good idea for to-one associations as it would lead to 2 separate SQL selects to initialize the association. " +
"This is expected to be improved in future versions of Hibernate"
)
void lazyGroupIgnoredForToOne(String ownerName, String attributeName, String requestedLazyGroup);
}

View File

@ -13,7 +13,7 @@ import java.util.Set;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.bytecode.BytecodeLogger;
import org.hibernate.bytecode.BytecodeLogging;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SelfDirtinessTracker;
@ -170,7 +170,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
}
public Object forceInitialize(Object target, String attributeName) {
BytecodeLogger.LOGGER.tracef(
BytecodeLogging.LOGGER.tracef(
"EnhancementAsProxyLazinessInterceptor#forceInitialize : %s#%s -> %s )",
entityKey.getEntityName(),
entityKey.getIdentifier(),
@ -186,7 +186,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
}
public Object forceInitialize(Object target, String attributeName, SharedSessionContractImplementor session, boolean isTemporarySession) {
BytecodeLogger.LOGGER.tracef(
BytecodeLogging.LOGGER.tracef(
"EnhancementAsProxyLazinessInterceptor#forceInitialize : %s#%s -> %s )",
entityKey.getEntityName(),
entityKey.getIdentifier(),

View File

@ -11,12 +11,11 @@ import java.util.function.BiFunction;
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.Collection;
import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
@ -25,19 +24,26 @@ import org.hibernate.mapping.Value;
* @author Steve Ebersole
*/
public class EnhancementHelper {
@FunctionalInterface
public interface InheritanceChecker {
boolean hasSubclasses(String entityName);
}
/**
* Should the given property be included in the owner's base fetch group?
*/
public static boolean includeInBaseFetchGroup(
Property bootMapping,
boolean isEnhanced,
InheritanceChecker inheritanceChecker,
boolean collectionsInDefaultFetchGroupEnabled) {
final Value value = bootMapping.getValue();
if ( ! isEnhanced ) {
if ( value instanceof ToOne ) {
if ( ( (ToOne) value ).isUnwrapProxy() ) {
BytecodeLogger.LOGGER.debugf(
BytecodeInterceptorLogging.MESSAGE_LOGGER.debugf(
"To-one property `%s#%s` was mapped with LAZY + NO_PROXY but the class was not enhanced",
bootMapping.getPersistentClass().getEntityName(),
bootMapping.getName()
@ -47,27 +53,89 @@ public class EnhancementHelper {
return true;
}
// if we get here, we know the property owner is enhanced for laziness
//
// NOTE : we make the (potentially untrue) assumption here that
// if the owner is enhanced, then all classes are enhanced..
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;
// ^^ previously we had to explicitly enable use of enhanced proxies.
// for the moment just return `true` assuming we can.
//
// there are cases where this block overall misses quite a few cases
// where it returns the least optimal value. This is true even outside
// this enhanced-proxy point
//
// those will all be addressed in the commits for HHH-13658
return true;
}
if ( ! toOne.isLazy() ) {
// its not lazy... select it
return true;
}
// it is lazy. see if we should select the FK
if ( bootMapping.getLazyGroup() != null ) {
// a non-base fetch group was explicitly specified
//
// really this should indicate to not select it as part of the base group.
// however, at the time being that leads to inefficient SQL - so for now
// we simply log a message that we are ignoring the `@LazyGroup` for to-ones
BytecodeInterceptorLogging.MESSAGE_LOGGER.lazyGroupIgnoredForToOne(
bootMapping.getPersistentClass().getEntityName(),
bootMapping.getName(),
bootMapping.getLazyGroup()
);
// at a later time - for example 6.0 when we can implement the join solution
// todo (6.0) : implement this
// return false;
// for now, fall through
}
if ( ! toOne.isReferenceToPrimaryKey() ) {
// we do not have a reference to the associated primary-key
return false;
}
if ( toOne.getColumnSpan() == 0 ) {
// generally this would indicate a "shared PK" on-to-one and there
// is no column for the association on the owner table - do not add
// the association to the base group (which would force an immediate
// select from the association table effectively making this
// association non-lazy)
return false;
}
final boolean unwrapExplicitlyRequested = toOne.isUnwrapProxy() && !toOne.isUnwrapProxyImplicit();
if ( inheritanceChecker.hasSubclasses( toOne.getReferencedEntityName() ) ) {
// the associated type has subclasses - we cannot use the enhanced proxy and will generate a HibernateProxy
if ( unwrapExplicitlyRequested ) {
// NO_PROXY was explicitly requested
BytecodeInterceptorLogging.LOGGER.debugf(
"`%s#%s` was mapped with LAZY and explicit NO_PROXY but the associated entity (`%s`) has subclasses",
bootMapping.getPersistentClass().getEntityName(),
bootMapping.getName(),
toOne.getReferencedEntityName()
);
}
// however, select the fk to create the enhanced-proxy
return true;
}
if ( toOne instanceof ManyToOne && ( (ManyToOne) toOne ).isIgnoreNotFound() ) {
if ( unwrapExplicitlyRequested ) {
BytecodeInterceptorLogging.LOGGER.debugf(
"%s#%s specified NotFoundAction.IGNORE & LazyToOneOption.NO_PROXY; " +
"skipping FK selection to more efficiently handle NotFoundAction.IGNORE",
bootMapping.getPersistentClass().getEntityName(),
bootMapping.getName()
);
return false;
}
}
if ( unwrapExplicitlyRequested ) {
// NO_PROXY was explicitly requested...
// - we return true here based on the assumption that `isEnhanced`
// is true for all classes - select the FK so we can build the enhanced-proxy
return true;
}
return true;
@ -118,7 +186,7 @@ public class EnhancementHelper {
// If we are using a temporary Session, begin a transaction if necessary
if ( isTempSession ) {
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork started temporary Session" );
BytecodeInterceptorLogging.LOGGER.debug( "Enhancement interception Helper#performWork started temporary Session" );
isJta = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
@ -128,7 +196,7 @@ public class EnhancementHelper {
// 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" );
BytecodeInterceptorLogging.LOGGER.debug( "Enhancement interception Helper#performWork starting transaction on temporary Session" );
session.beginTransaction();
}
}
@ -142,12 +210,12 @@ public class EnhancementHelper {
try {
// Commit the JDBC transaction if we started one.
if ( !isJta ) {
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork committing transaction on temporary Session" );
BytecodeInterceptorLogging.LOGGER.debug( "Enhancement interception Helper#performWork committing transaction on temporary Session" );
session.getTransaction().commit();
}
}
catch (Exception e) {
BytecodeLogger.LOGGER.warn(
BytecodeInterceptorLogging.LOGGER.warn(
"Unable to commit JDBC transaction on temporary session used to load lazy " +
"collection associated to no session"
);
@ -155,11 +223,11 @@ public class EnhancementHelper {
// Close the just opened temp Session
try {
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork closing temporary Session" );
BytecodeInterceptorLogging.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" );
BytecodeInterceptorLogging.LOGGER.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
}
}
}

View File

@ -127,7 +127,7 @@ public class LazyAttributeLoadingInterceptor extends AbstractLazyLoadInterceptor
}
public boolean hasAnyUninitializedAttributes() {
if ( lazyFields == null ) {
if ( lazyFields == null || lazyFields.isEmpty() ) {
return false;
}

View File

@ -17,8 +17,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.persister.spi.PersisterCreationContext;
/**
* Information about all of the bytecode lazy attributes for an entity
@ -34,7 +36,8 @@ public class LazyAttributesMetadata implements Serializable {
public static LazyAttributesMetadata from(
PersistentClass mappedEntity,
boolean isEnhanced,
boolean collectionsInDefaultFetchGroupEnabled) {
boolean collectionsInDefaultFetchGroupEnabled,
PersisterCreationContext creationContext) {
final Map<String, LazyAttributeDescriptor> lazyAttributeDescriptorMap = new LinkedHashMap<>();
final Map<String, Set<String>> fetchGroupToAttributesMap = new HashMap<>();
@ -47,6 +50,12 @@ public class LazyAttributesMetadata implements Serializable {
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
property,
isEnhanced,
(entityName) -> {
final MetadataImplementor metadata = creationContext.getMetadata();
final PersistentClass entityBinding = metadata.getEntityBinding( entityName );
assert entityBinding != null;
return entityBinding.hasSubclasses();
},
collectionsInDefaultFetchGroupEnabled
);
if ( lazy ) {

View File

@ -3248,7 +3248,8 @@ public final class AnnotationBinder {
}
else {
toOne.setLazy( fetchType == FetchType.LAZY );
toOne.setUnwrapProxy( false );
toOne.setUnwrapProxy( fetchType != FetchType.LAZY );
toOne.setUnwrapProxyImplicit( true );
}
if ( fetch != null ) {
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {

View File

@ -39,6 +39,26 @@ public enum OptimisticLockStyle {
return oldCode;
}
public boolean isAllOrDirty() {
return isAll() || isDirty();
}
public boolean isAll() {
return this == ALL;
}
public boolean isDirty() {
return this == DIRTY;
}
public boolean isVersion() {
return this == VERSION;
}
public boolean isNone() {
return this == NONE;
}
/**
* Given an old code (one of the int constants from Cascade), interpret it as one of the new enums.
*

View File

@ -13,6 +13,9 @@ import org.hibernate.CacheMode;
import org.hibernate.IdentifierLoadAccess;
import org.hibernate.LockOptions;
import org.hibernate.ObjectNotFoundException;
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.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.spi.EventSource;
@ -22,6 +25,8 @@ import org.hibernate.graph.GraphSemantic;
import org.hibernate.graph.RootGraph;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
/**
* @author Steve Ebersole
@ -152,7 +157,10 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
if ( this.lockOptions != null ) {
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, eventSource, loadQueryInfluencers.getReadOnly() );
context.fireLoad( event, LoadEventListener.GET );
return (T) event.getResult();
final Object result = event.getResult();
initializeIfNecessary( result );
return (T) result;
}
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, eventSource, loadQueryInfluencers.getReadOnly() );
@ -167,6 +175,35 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
finally {
context.afterOperation( success );
}
return (T) event.getResult();
final Object result = event.getResult();
initializeIfNecessary( result );
return (T) result;
}
private void initializeIfNecessary(Object result) {
if ( result == null ) {
return;
}
if ( result instanceof HibernateProxy ) {
final HibernateProxy hibernateProxy = (HibernateProxy) result;
final LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer();
if ( initializer.isUninitialized() ) {
initializer.initialize();
}
return;
}
final BytecodeEnhancementMetadata enhancementMetadata = entityPersister.getEntityMetamodel().getBytecodeEnhancementMetadata();
if ( ! enhancementMetadata.isEnhancedForLazyLoading() ) {
return;
}
final BytecodeLazyAttributeInterceptor interceptor = enhancementMetadata.extractLazyInterceptor( result);
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( result, null );
}
}
}

View File

@ -28,6 +28,7 @@ public abstract class ToOne extends SimpleValue implements Fetchable {
private String propertyName;
private boolean lazy = true;
protected boolean unwrapProxy;
protected boolean isUnwrapProxyImplicit;
protected boolean referenceToPrimaryKey = true;
protected ToOne(MetadataBuildingContext buildingContext, Table table) {
@ -123,6 +124,18 @@ public abstract class ToOne extends SimpleValue implements Fetchable {
this.unwrapProxy = unwrapProxy;
}
public boolean isUnwrapProxyImplicit() {
return isUnwrapProxyImplicit;
}
/**
* Related to HHH-13658 - keep track of whether `unwrapProxy` is an implicit value
* for reference later
*/
public void setUnwrapProxyImplicit(boolean unwrapProxyImplicit) {
isUnwrapProxyImplicit = unwrapProxyImplicit;
}
public boolean isReferenceToPrimaryKey() {
return referenceToPrimaryKey;
}

View File

@ -46,6 +46,7 @@ import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
@ -219,7 +220,6 @@ import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer;
import org.hibernate.sql.results.graph.entity.internal.EntityResultImpl;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.tuple.GenerationTiming;
@ -705,7 +705,7 @@ public abstract class AbstractEntityPersister
this.naturalIdRegionAccessStrategy = null;
}
this.entityMetamodel = new EntityMetamodel( bootDescriptor, this, factory );
this.entityMetamodel = new EntityMetamodel( bootDescriptor, this, creationContext );
if ( entityMetamodel.isMutable() ) {
this.entityEntryFactory = MutableEntityEntryFactory.INSTANCE;
@ -867,6 +867,12 @@ public abstract class AbstractEntityPersister
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
prop,
entityMetamodel.isInstrumented(),
(entityName) -> {
final MetadataImplementor metadata = creationContext.getMetadata();
final PersistentClass entityBinding = metadata.getEntityBinding( entityName );
assert entityBinding != null;
return entityBinding.hasSubclasses();
},
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled()
);
@ -946,6 +952,12 @@ public abstract class AbstractEntityPersister
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
prop,
entityMetamodel.isInstrumented(),
(entityName) -> {
final MetadataImplementor metadata = creationContext.getMetadata();
final PersistentClass entityBinding = metadata.getEntityBinding( entityName );
assert entityBinding != null;
return entityBinding.hasSubclasses();
},
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled()
);
while ( colIter.hasNext() ) {
@ -1795,8 +1807,8 @@ public abstract class AbstractEntityPersister
}
public boolean isBatchable() {
return optimisticLockStyle() == OptimisticLockStyle.NONE
|| ( !isVersioned() && optimisticLockStyle() == OptimisticLockStyle.VERSION )
return optimisticLockStyle().isNone()
|| !isVersioned() && optimisticLockStyle().isVersion()
|| getFactory().getSessionFactoryOptions().isJdbcBatchVersionedData();
}
@ -2011,14 +2023,13 @@ public abstract class AbstractEntityPersister
// rather than trying to handle the individual generated portions.
String selectClause = concretePropertySelectFragment(
getRootAlias(),
new InclusionChecker() {
@Override
public boolean includeProperty(int propertyNumber) {
final InDatabaseValueGenerationStrategy generationStrategy
= entityMetamodel.getInDatabaseValueGenerationStrategies()[propertyNumber];
return generationStrategy != null
&& timingsMatch( generationStrategy.getGenerationTiming(), generationTimingToMatch );
}
propertyNumber -> {
final InDatabaseValueGenerationStrategy generationStrategy
= entityMetamodel.getInDatabaseValueGenerationStrategies()[propertyNumber];
GenerationTiming timing = generationStrategy.getGenerationTiming();
return generationStrategy != null
&& (generationTimingToMatch == GenerationTiming.INSERT && timing.includesInsert()
|| generationTimingToMatch == GenerationTiming.ALWAYS && timing.includesUpdate());
}
);
selectClause = selectClause.substring( 2 );
@ -2046,11 +2057,7 @@ public abstract class AbstractEntityPersister
protected String concretePropertySelectFragment(String alias, final boolean[] includeProperty) {
return concretePropertySelectFragment(
alias,
new InclusionChecker() {
public boolean includeProperty(int propertyNumber) {
return includeProperty[propertyNumber];
}
}
propertyNumber -> includeProperty[propertyNumber]
);
}
@ -2922,7 +2929,7 @@ public abstract class AbstractEntityPersister
update.addPrimaryKeyColumns( getKeyColumns( j ) );
}
if ( j == 0 && isVersioned() && entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.VERSION ) {
if ( j == 0 && isVersioned() && entityMetamodel.getOptimisticLockStyle().isVersion() ) {
// this is the root (versioned) table, and we are using version-based
// optimistic locking; if we are not updating the version, also don't
// check it (unless this is a "generated" version column)!
@ -2934,12 +2941,11 @@ public abstract class AbstractEntityPersister
else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
// we are using "all" or "dirty" property-based optimistic locking
boolean[] includeInWhere = entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL
?
getPropertyUpdateability()
boolean[] includeInWhere = entityMetamodel.getOptimisticLockStyle().isAll()
//optimistic-lock="all", include all updatable properties
:
includeProperty; //optimistic-lock="dirty", include all properties we are updating this time
? getPropertyUpdateability()
//optimistic-lock="dirty", include all properties we are updating this time
: includeProperty;
boolean[] versionability = getPropertyVersionability();
Type[] types = getPropertyTypes();
@ -3666,14 +3672,14 @@ public abstract class AbstractEntityPersister
);
// Write any appropriate versioning conditional parameters
if ( useVersion && entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.VERSION ) {
if ( useVersion && entityMetamodel.getOptimisticLockStyle().isVersion()) {
if ( checkVersion( includeProperty ) ) {
getVersionType().nullSafeSet( update, oldVersion, index, session );
}
}
else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
boolean[] versionability = getPropertyVersionability(); //TODO: is this really necessary????
boolean[] includeOldField = entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL
boolean[] includeOldField = entityMetamodel.getOptimisticLockStyle().isAll()
? getPropertyUpdateability()
: includeProperty;
Type[] types = getPropertyTypes();
@ -4089,11 +4095,10 @@ public abstract class AbstractEntityPersister
}
protected boolean isAllOrDirtyOptLocking() {
return entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.DIRTY
|| entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL;
return entityMetamodel.getOptimisticLockStyle().isAllOrDirty();
}
private String[] generateSQLDeleteStrings(Object[] loadedState) {
protected String[] generateSQLDeleteStrings(Object[] loadedState) {
int span = getTableSpan();
String[] deleteStrings = new String[span];
for ( int j = span - 1; j >= 0; j-- ) {
@ -4664,7 +4669,7 @@ public abstract class AbstractEntityPersister
return null;
}
else if ( interceptor.isAttributeLoaded( nameOfAttributeBeingAccessed ) ) {
value = getEntityTuplizer().getPropertyValue( entity, nameOfAttributeBeingAccessed );
value = getPropertyValue( entity, nameOfAttributeBeingAccessed );
}
else {
value = ( (LazyPropertyInitializer) this ).initializeLazyProperty( nameOfAttributeBeingAccessed, entity, session );
@ -5648,12 +5653,11 @@ public abstract class AbstractEntityPersister
}
private boolean isValueGenerationRequired(NonIdentifierAttribute attribute, GenerationTiming matchTiming) {
if ( attribute.getType() instanceof ComponentType ) {
public static boolean isValueGenerationRequired(NonIdentifierAttribute attribute, GenerationTiming matchTiming) {
if ( attribute.getType() instanceof ComponentType) {
final ComponentType type = (ComponentType) attribute.getType();
final ValueGeneration[] propertyValueGenerationStrategies = type.getPropertyValueGenerationStrategies();
for ( ValueGeneration propertyValueGenerationStrategie : propertyValueGenerationStrategies ) {
if ( isReadRequired( propertyValueGenerationStrategie, matchTiming ) ) {
for ( ValueGeneration valueGenerationStrategy : type.getPropertyValueGenerationStrategies() ) {
if ( isReadRequired( valueGenerationStrategy, matchTiming ) ) {
return true;
}
}
@ -5667,16 +5671,10 @@ public abstract class AbstractEntityPersister
/**
* Whether the given value generation strategy requires to read the value from the database or not.
*/
private boolean isReadRequired(ValueGeneration valueGeneration, GenerationTiming matchTiming) {
return valueGeneration != null &&
valueGeneration.getValueGenerator() == null &&
timingsMatch( valueGeneration.getGenerationTiming(), matchTiming );
}
private boolean timingsMatch(GenerationTiming timing, GenerationTiming matchTiming) {
return
( matchTiming == GenerationTiming.INSERT && timing.includesInsert() ) ||
( matchTiming == GenerationTiming.ALWAYS && timing.includesUpdate() );
private static boolean isReadRequired(ValueGeneration valueGeneration, GenerationTiming matchTiming) {
return valueGeneration != null
&& valueGeneration.getValueGenerator() == null
&& valueGeneration.timingMatches( matchTiming );
}
public String getIdentifierPropertyName() {

View File

@ -26,7 +26,6 @@ import org.hibernate.QueryException;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -240,7 +239,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
discriminatorSQLString = null;
}
if ( optimisticLockStyle() == OptimisticLockStyle.ALL || optimisticLockStyle() == OptimisticLockStyle.DIRTY ) {
if ( optimisticLockStyle().isAllOrDirty() ) {
throw new MappingException( "optimistic-lock=all|dirty not supported for joined-subclass mappings [" + getEntityName() + "]" );
}

View File

@ -10,6 +10,7 @@ import java.lang.reflect.Constructor;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
import org.hibernate.engine.internal.UnsavedValueFactory;
@ -22,6 +23,7 @@ import org.hibernate.mapping.KeyValue;
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.property.access.spi.Getter;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.PropertyAccessStrategy;
@ -155,7 +157,8 @@ public final class PropertyFactory {
SessionFactoryImplementor sessionFactory,
int attributeNumber,
Property property,
boolean lazyAvailable) {
boolean lazyAvailable,
PersisterCreationContext creationContext) {
final Type type = property.getValue().getType();
final NonIdentifierAttributeNature nature = decode( type );
@ -174,6 +177,12 @@ public final class PropertyFactory {
final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
property,
lazyAvailable,
(entityName) -> {
final MetadataImplementor metadata = creationContext.getMetadata();
final PersistentClass entityBinding = metadata.getEntityBinding( entityName );
assert entityBinding != null;
return entityBinding.hasSubclasses();
},
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled()
);

View File

@ -53,4 +53,13 @@ public interface ValueGeneration extends Serializable {
* @return The column value to be used in the SQL.
*/
public String getDatabaseGeneratedReferencedColumnValue();
/**
* Does this value generation occur with the given timing?
*/
default boolean timingMatches(GenerationTiming timing) {
GenerationTiming generationTiming = getGenerationTiming();
return timing == GenerationTiming.INSERT && generationTiming.includesInsert()
|| timing == GenerationTiming.ALWAYS && generationTiming.includesUpdate();
}
}

View File

@ -25,6 +25,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.type.CompositeType;
/**
@ -38,11 +39,12 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
PersistentClass persistentClass,
Set<String> identifierAttributeNames,
CompositeType nonAggregatedCidMapper,
boolean collectionsInDefaultFetchGroupEnabled) {
boolean collectionsInDefaultFetchGroupEnabled,
PersisterCreationContext creationContext) {
final Class mappedClass = persistentClass.getMappedClass();
final boolean enhancedForLazyLoading = PersistentAttributeInterceptable.class.isAssignableFrom( mappedClass );
final LazyAttributesMetadata lazyAttributesMetadata = enhancedForLazyLoading
? LazyAttributesMetadata.from( persistentClass, true, collectionsInDefaultFetchGroupEnabled )
? LazyAttributesMetadata.from( persistentClass, true, collectionsInDefaultFetchGroupEnabled, creationContext )
: LazyAttributesMetadata.nonEnhanced( persistentClass.getEntityName() );
return new BytecodeEnhancementMetadataPojoImpl(

View File

@ -19,6 +19,7 @@ import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
@ -35,6 +36,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;
@ -129,8 +131,8 @@ public class EntityMetamodel implements Serializable {
public EntityMetamodel(
PersistentClass persistentClass,
EntityPersister persister,
SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
PersisterCreationContext creationContext) {
this.sessionFactory = creationContext.getSessionFactory();
name = persistentClass.getEntityName();
rootName = persistentClass.getRootClass().getEntityName();
@ -167,7 +169,8 @@ public class EntityMetamodel implements Serializable {
persistentClass,
idAttributeNames,
nonAggregatedCidMapper,
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled()
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled(),
creationContext
);
}
else {
@ -230,7 +233,8 @@ public class EntityMetamodel implements Serializable {
sessionFactory,
i,
prop,
bytecodeEnhancementMetadata.isEnhancedForLazyLoading()
bytecodeEnhancementMetadata.isEnhancedForLazyLoading(),
creationContext
);
}
@ -249,6 +253,12 @@ public class EntityMetamodel implements Serializable {
boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup(
prop,
bytecodeEnhancementMetadata.isEnhancedForLazyLoading(),
(entityName) -> {
final MetadataImplementor metadata = creationContext.getMetadata();
final PersistentClass entityBinding = metadata.getEntityBinding( entityName );
assert entityBinding != null;
return entityBinding.hasSubclasses();
},
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled()
);
@ -383,9 +393,7 @@ public class EntityMetamodel implements Serializable {
hasSubclasses = persistentClass.hasSubclasses();
optimisticLockStyle = persistentClass.getOptimisticLockStyle();
final boolean isAllOrDirty =
optimisticLockStyle == OptimisticLockStyle.ALL
|| optimisticLockStyle == OptimisticLockStyle.DIRTY;
final boolean isAllOrDirty = optimisticLockStyle.isAllOrDirty();
if ( isAllOrDirty && !dynamicUpdate ) {
throw new MappingException( "optimistic-lock=all|dirty requires dynamic-update=\"true\": " + name );
}

View File

@ -0,0 +1,12 @@
/*
* 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 related to {@link org.hibernate.annotations.LazyToOne}, especially
* regarding handling of {@link org.hibernate.annotations.LazyToOneOption#NO_PROXY}
*/
package org.hibernate.orm.test.mapping.lazytoone;

View File

@ -21,19 +21,26 @@ import org.hibernate.Hibernate;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.Configuration;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.hibernate.testing.transaction.TransactionUtil2.fromTransaction;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
@ -42,6 +49,7 @@ import static org.junit.Assert.assertTrue;
@TestForIssue(jiraKey = "HHH-10252")
@RunWith(BytecodeEnhancerRunner.class)
public class CascadeDeleteManyToOneTest extends BaseCoreFunctionalTestCase {
private SQLStatementInterceptor sqlInterceptor;
private Child originalChild;
@Override
@ -49,6 +57,12 @@ public class CascadeDeleteManyToOneTest extends BaseCoreFunctionalTestCase {
return new Class[] { Parent.class, Child.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
sqlInterceptor = new SQLStatementInterceptor( configuration );
}
@Before
public void prepare() {
// Create a Parent with one Child
@ -64,46 +78,28 @@ public class CascadeDeleteManyToOneTest extends BaseCoreFunctionalTestCase {
);
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void testManagedWithUninitializedAssociation() {
// Delete the Child
doInHibernate(
this::sessionFactory, s -> {
Child loadedChild = (Child) s.createQuery( "SELECT c FROM Child c WHERE name=:name" )
.setParameter( "name", "CHILD" )
.uniqueResult();
checkInterceptor( loadedChild, false );
assertFalse( Hibernate.isPropertyInitialized( loadedChild, "parent" ) );
s.delete( loadedChild );
}
);
// Explicitly check that both got deleted
doInHibernate(
this::sessionFactory, s -> {
assertNull( s.createQuery( "FROM Child c" ).uniqueResult() );
assertNull( s.createQuery( "FROM Parent p" ).uniqueResult() );
}
);
}
@Test
public void testManagedWithInitializedAssociation() {
sqlInterceptor.clear();
// Delete the Child
doInHibernate(
this::sessionFactory, s -> {
Child loadedChild = (Child) s.createQuery( "SELECT c FROM Child c WHERE name=:name" )
inTransaction(
(s) -> {
final Child managedChild = (Child) s.createQuery( "SELECT c FROM Child c WHERE name=:name" )
.setParameter( "name", "CHILD" )
.uniqueResult();
checkInterceptor( loadedChild, false );
loadedChild.getParent();
assertTrue( Hibernate.isPropertyInitialized( loadedChild, "parent" ) );
s.delete( loadedChild );
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
// parent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( managedChild, "parent" ) );
assertThat( managedChild.getParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( managedChild.getParent() ) );
s.delete( managedChild );
}
);
// Explicitly check that both got deleted
doInHibernate(
this::sessionFactory, s -> {
@ -114,49 +110,21 @@ public class CascadeDeleteManyToOneTest extends BaseCoreFunctionalTestCase {
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void testDetachedWithUninitializedAssociation() {
final Child detachedChild = doInHibernate(
this::sessionFactory, s -> {
return s.get( Child.class, originalChild.getId() );
}
);
assertFalse( Hibernate.isPropertyInitialized( detachedChild, "parent" ) );
checkInterceptor( detachedChild, false );
// Delete the detached Child with uninitialized parent
doInHibernate(
this::sessionFactory, s -> {
s.delete( detachedChild );
}
);
// Explicitly check that both got deleted
doInHibernate(
this::sessionFactory, s -> {
assertNull( s.createQuery( "FROM Child c" ).uniqueResult() );
assertNull( s.createQuery( "FROM Parent p" ).uniqueResult() );
}
);
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void testDetachedWithInitializedAssociation() {
final Child detachedChild = doInHibernate(
this::sessionFactory, s -> {
Child child = s.get( Child.class, originalChild.getId() );
assertFalse( Hibernate.isPropertyInitialized( child, "parent" ) );
sqlInterceptor.clear();
final Child detachedChild = fromTransaction(
sessionFactory(),
(s) -> {
Child child = s.get( Child.class, originalChild.getId() );
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
// parent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( child, "parent" ) );
assertThat( child.getParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( child.getParent() ) );
// initialize parent before detaching
child.getParent();
return child;
}
);
@ -166,14 +134,13 @@ public class CascadeDeleteManyToOneTest extends BaseCoreFunctionalTestCase {
checkInterceptor( detachedChild, false );
// Delete the detached Child with initialized parent
doInHibernate(
this::sessionFactory, s -> {
s.delete( detachedChild );
}
inTransaction(
(s) -> s.delete( detachedChild )
);
// Explicitly check that both got deleted
doInHibernate(
this::sessionFactory, s -> {
inTransaction(
(s) -> {
assertNull( s.createQuery( "FROM Child c" ).uniqueResult() );
assertNull( s.createQuery( "FROM Parent p" ).uniqueResult() );
}
@ -204,8 +171,7 @@ public class CascadeDeleteManyToOneTest extends BaseCoreFunctionalTestCase {
}
private void checkInterceptor(Child child, boolean isNullExpected) {
final BytecodeEnhancementMetadata bytecodeEnhancementMetadata =
sessionFactory()
final BytecodeEnhancementMetadata bytecodeEnhancementMetadata = sessionFactory()
.getMetamodel()
.entityPersister( Child.class )
.getEntityMetamodel()

View File

@ -18,18 +18,23 @@ import org.hibernate.Hibernate;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.testing.transaction.TransactionUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
@ -39,81 +44,97 @@ import static org.junit.Assert.assertTrue;
@RunWith(BytecodeEnhancerRunner.class)
@TestForIssue(jiraKey = "HHH-13129")
public class CascadeOnUninitializedTest extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlInterceptor;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Address.class,
};
return new Class<?>[] { Person.class, Address.class };
}
@Override
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( AvailableSettings.SHOW_SQL, "true" );
settings.put( AvailableSettings.FORMAT_SQL, "true" );
sqlInterceptor = new SQLStatementInterceptor( settings );
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void testMergeDetachedEnhancedEntityWithUninitializedManyToOne() {
final Person person = persistPersonWithManyToOne();
Person person = persistPersonWithManyToOne();
sqlInterceptor.clear();
// get a detached Person
Person detachedPerson = TransactionUtil.doInHibernate(
this::sessionFactory, session -> {
return session.get( Person.class, person.getId() );
}
final Person detachedPerson = fromTransaction(
session -> session.get( Person.class, person.getId() )
);
// address should not be initialized
assertFalse( Hibernate.isPropertyInitialized( detachedPerson, "primaryAddress" ) );
// loading Person should lead to one SQL
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
// primaryAddress should be "initialized" as an enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( detachedPerson, "primaryAddress" ) );
assertThat( detachedPerson.getPrimaryAddress(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( detachedPerson.getPrimaryAddress() ) );
// alter the detached reference
detachedPerson.setName( "newName" );
Person mergedPerson = TransactionUtil.doInHibernate(
this::sessionFactory, session -> {
return (Person) session.merge( detachedPerson );
}
final Person mergedPerson = fromTransaction(
session -> (Person) session.merge( detachedPerson )
);
// address should not be initialized
assertFalse( Hibernate.isPropertyInitialized( mergedPerson, "primaryAddress" ) );
// 1) select Person#addresses
// 2) select Person#primaryAddress
// 3) update Person
assertThat( sqlInterceptor.getQueryCount(), is( 3 ) );
// primaryAddress should not be initialized
assertTrue( Hibernate.isPropertyInitialized( detachedPerson, "primaryAddress" ) );
assertThat( detachedPerson.getPrimaryAddress(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( detachedPerson.getPrimaryAddress() ) );
assertEquals( "newName", mergedPerson.getName() );
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void testDeleteEnhancedEntityWithUninitializedManyToOne() {
Person person = persistPersonWithManyToOne();
sqlInterceptor.clear();
// get a detached Person
Person detachedPerson = TransactionUtil.doInHibernate(
this::sessionFactory, session -> {
return session.get( Person.class, person.getId() );
}
Person detachedPerson = fromTransaction(
session -> session.get( Person.class, person.getId() )
);
// address should not be initialized
assertFalse( Hibernate.isPropertyInitialized( detachedPerson, "primaryAddress" ) );
// loading Person should lead to one SQL
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
// deleting detachedPerson should result in detachedPerson.address being initialized,
// primaryAddress should be initialized as an enhance-proxy
assertTrue( Hibernate.isPropertyInitialized( detachedPerson, "primaryAddress" ) );
assertThat( detachedPerson, not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( detachedPerson.getPrimaryAddress() ) );
sqlInterceptor.clear();
// deleting detachedPerson should result in detachedPerson.primaryAddress being initialized,
// so that the DELETE operation can be cascaded to it.
TransactionUtil.doInHibernate(
this::sessionFactory, session -> {
session.delete( detachedPerson );
}
inTransaction(
session -> session.delete( detachedPerson )
);
// 1) select Person#addresses
// 2) select Person#primaryAddress
// 3) delete Person
// 4) select primary Address
assertThat( sqlInterceptor.getQueryCount(), is( 4 ) );
// both the Person and its Address should be deleted
TransactionUtil.doInHibernate(
this::sessionFactory, session -> {
inTransaction(
session -> {
assertNull( session.get( Person.class, person.getId() ) );
assertNull( session.get( Person.class, person.getPrimaryAddress().getId() ) );
}
@ -223,12 +244,12 @@ public class CascadeOnUninitializedTest extends BaseNonConfigCoreFunctionalTestC
private String name;
@ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
@JoinColumn(name = "ADDRESS_ID")
@JoinColumn(name = "primary_address_id")
@LazyToOne(LazyToOneOption.NO_PROXY)
private Address primaryAddress;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn
@JoinColumn( name = "person_id" )
private Set<Address> addresses = new HashSet<>();
public Long getId() {

View File

@ -20,13 +20,14 @@ import org.hibernate.Session;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
@ -35,7 +36,10 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@ -54,21 +58,26 @@ import static org.junit.Assert.assertTrue;
EnhancerTestContext.class, // supports laziness and dirty-checking
BidirectionalLazyTest.NoDirtyCheckEnhancementContext.class // supports laziness; does not support dirty-checking
})
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
// NOTE : tests in this class seem redundant because they assert things that happened
// in previous versions that have been fixed
public Class<?>[] getAnnotatedClasses() {
return new Class[] { Employer.class, Employee.class, Unrelated.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
}
@Test
public void testRemoveWithDeletedAssociatedEntity() {
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
Employer employer = new Employer( "RedHat" );
session.persist( employer );
@ -81,27 +90,27 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(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 );
// Should be initialized because at least one entity was deleted beforehand
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
session.remove( employee );
assertSame( employer, employee.getEmployer() );
// employee.getEmployer was initialized, and should be nullified in EntityEntry#deletedState
checkEntityEntryState( session, employee, employer, true );
}
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
assertNull( session.find( Employer.class, "RedHat" ) );
assertTrue( session.createQuery( "from Employee e", Employee.class ).getResultList().isEmpty() );
}
@ -111,8 +120,8 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
@Test
public void testRemoveWithNonAssociatedRemovedEntity() {
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
Employer employer = new Employer( "RedHat" );
session.persist( employer );
Employee employee = new Employee( "Jack" );
@ -122,22 +131,22 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
// Delete an entity that is not associated with Employee
session.remove( session.get( Unrelated.class, 1 ) );
final Employee employee = session.get( Employee.class, "Jack" );
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
session.remove( employee );
// Should be initialized because at least one entity was deleted beforehand
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
session.remove( employee );
// employee.getEmployer was initialized, and should not be nullified in EntityEntry#deletedState
checkEntityEntryState( session, employee, employee.getEmployer(), false );
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
assertNull( session.find( Unrelated.class, 1 ) );
assertNull( session.find( Employee.class, "Jack" ) );
session.remove( session.find( Employer.class, "RedHat" ) );
@ -148,8 +157,8 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
@Test
public void testRemoveWithNoRemovedEntities() {
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
Employer employer = new Employer( "RedHat" );
session.persist( employer );
Employee employee = new Employee( "Jack" );
@ -158,22 +167,25 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
// Don't delete any entities before deleting the Employee
final Employee employee = session.get( Employee.class, "Jack" );
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
verifyBaseState( employee );
session.remove( employee );
// There were no other deleted entities before employee was deleted,
// so there was no need to initialize employee.employer.
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
// employee.getEmployer was not initialized, and should not be nullified in EntityEntry#deletedState
checkEntityEntryState( session, employee, LazyPropertyInitializer.UNFETCHED_PROPERTY, false );
Employer employer = session.get( Employer.class, "RedHat" );
verifyBaseState( employer );
assertThat( employee.getEmployer(), sameInstance( employer ) );
checkEntityEntryState( session, employee, employer, false );
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
assertNull( session.find( Employee.class, "Jack" ) );
session.remove( session.find( Employer.class, "RedHat" ) );
}
@ -183,8 +195,8 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
@Test
public void testRemoveEntityWithNullLazyManyToOne() {
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
Employer employer = new Employer( "RedHat" );
session.persist( employer );
Employee employee = new Employee( "Jack" );
@ -192,22 +204,17 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
Employee employee = session.get( Employee.class, "Jack" );
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
verifyBaseState( employee );
// Get and delete an Employer that is not associated with employee
Employer employer = session.get( Employer.class, "RedHat" );
session.remove( employer );
verifyBaseState( 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( employer );
session.remove( employee );
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
}
);
}
@ -231,23 +238,37 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
inTransaction(
session -> {
Employee employee = session.get( Employee.class, "Jack" );
assertFalse( Hibernate.isPropertyInitialized( employee, "employer" ) );
verifyBaseState( employee );
// Get and delete an Employer that is not associated with employee
Employer employer = session.get( Employer.class, "RedHat" );
session.remove( employer );
verifyBaseState( 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" ) );
assertThat( employee.getEmployer(), sameInstance( employer ) );
session.remove( employer );
session.remove( employee );
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
}
);
}
private void verifyBaseState(Employer employer) {
assertTrue( Hibernate.isPropertyInitialized( employer, "name" ) );
}
private void verifyBaseState(Employee employee) {
assertTrue( Hibernate.isPropertyInitialized( employee, "name" ) );
// employer will be either null or an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
final Employer employer = employee.getEmployer();
if ( employer != null ) {
assertFalse( Hibernate.isInitialized( employer ) );
assertThat(employer, not( instanceOf( HibernateProxy.class ) ) );
}
}
private void checkEntityEntryState(
final Session session,
final Employee employee,
@ -261,7 +282,7 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
entityEntry.getLoadedState()[propertyNumber]
);
if ( isEmployerNullified ) {
assertEquals( null, entityEntry.getDeletedState()[propertyNumber] );
assertNull( entityEntry.getDeletedState()[ propertyNumber ] );
}
else {
assertEquals(
@ -372,7 +393,7 @@ public class BidirectionalLazyTest extends BaseCoreFunctionalTestCase {
return true;
}
else if ( o instanceof Employee ) {
Employee other = Employee.class.cast( o );
Employee other = (Employee) o;
if ( name != null ) {
return getName().equals( other.getName() );
}

View File

@ -21,7 +21,6 @@ import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
@ -33,6 +32,8 @@ import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Gail Badner
@ -44,36 +45,39 @@ import static org.junit.Assert.assertFalse;
public class NaturalIdInUninitializedAssociationTest extends BaseNonConfigCoreFunctionalTestCase {
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void testImmutableNaturalId() {
public void testLoad() {
inTransaction(
session -> {
final AnEntity e = session.get( AnEntity.class, 3 );
assertFalse( Hibernate.isPropertyInitialized( e,"entityImmutableNaturalId" ) );
}
);
final AnEntity e = session.byId( AnEntity.class ).load(3 );
assertTrue( Hibernate.isInitialized( e ) );
inTransaction(
session -> {
final AnEntity e = session.get( AnEntity.class, 3 );
// because we can (enhanced) proxy both EntityMutableNaturalId and EntityImmutableNaturalId
// we will select their FKs and all attributes will be "bytecode initialized"
assertTrue( Hibernate.isPropertyInitialized( e,"entityMutableNaturalId" ) );
assertTrue( Hibernate.isPropertyInitialized( e,"entityImmutableNaturalId" ) );
assertEquals( "mutable name", e.entityMutableNaturalId.name );
assertEquals( "immutable name", e.entityImmutableNaturalId.name );
}
);
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void testMutableNaturalId() {
public void testGetReference() {
inTransaction(
session -> {
final AnEntity e = session.get( AnEntity.class, 3 );
assertFalse( Hibernate.isPropertyInitialized( e,"entityMutableNaturalId" ) );
final AnEntity e = session.byId( AnEntity.class ).getReference( 3 );
assertFalse( Hibernate.isInitialized( e ) );
// trigger initialization
Hibernate.initialize( e );
// silly, but...
assertTrue( Hibernate.isInitialized( e ) );
// because we can (enhanced) proxy both EntityMutableNaturalId and EntityImmutableNaturalId
// we will select their FKs and all attributes will be "bytecode initialized"
assertTrue( Hibernate.isPropertyInitialized( e,"entityMutableNaturalId" ) );
assertTrue( Hibernate.isPropertyInitialized( e,"entityImmutableNaturalId" ) );
}
);

View File

@ -1,20 +1,16 @@
package org.hibernate.test.bytecode.enhancement.lazy.cache;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.AssertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Cache;
@ -23,18 +19,21 @@ import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.stat.CacheRegionStatistics;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.transaction.TransactionUtil;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(BytecodeEnhancerRunner.class)
public class UninitializedAssociationsInCacheTest extends BaseCoreFunctionalTestCase {
@ -53,33 +52,21 @@ public class UninitializedAssociationsInCacheTest extends BaseCoreFunctionalTest
@Test
@TestForIssue( jiraKey = "HHH-11766")
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void attributeLoadingFromCache() {
final AtomicLong bossId = new AtomicLong();
final AtomicLong teamleaderId = new AtomicLong();
final AtomicLong teammemberId = new AtomicLong();
TransactionUtil.doInHibernate(
this::sessionFactory, (s) -> {
Employee boss = new Employee();
Employee teamleader = new Employee();
Employee teammember = new Employee();
boss.regularString = "boss";
teamleader.regularString = "leader";
teammember.regularString = "member";
inTransaction(
(s) -> {
Employee boss = new Employee( 1, "boss" );
Employee teamleader = new Employee( 2, "leader" );
Employee teammember = new Employee( 3, "member" );
s.persist( boss );
s.persist( teamleader );
s.persist( teammember );
boss.subordinates.add( teamleader );
teamleader.superior = boss;
teamleader.subordinates.add( teammember );
teammember.superior = teamleader;
bossId.set( boss.id );
teamleaderId.set( teamleader.id );
teammemberId.set( teammember.id );
}
);
@ -87,19 +74,28 @@ public class UninitializedAssociationsInCacheTest extends BaseCoreFunctionalTest
sessionFactory().getStatistics().clear();
CacheRegionStatistics regionStatistics = sessionFactory().getStatistics().getCacheRegionStatistics( "Employee" );
TransactionUtil.doInHibernate(
this::sessionFactory, (s) -> {
final Employee boss = s.find( Employee.class, bossId.get() );
inTransaction(
(s) -> {
final Employee boss = s.find( Employee.class, 1 );
Assert.assertEquals( "boss", boss.regularString );
final Employee leader = s.find( Employee.class, teamleaderId.get() );
final Employee leader = s.find( Employee.class, 2 );
Assert.assertEquals( "leader", leader.regularString );
final Employee member = s.find( Employee.class, teammemberId.get() );
final Employee member = s.find( Employee.class, 3 );
Assert.assertEquals( "member", member.regularString );
Assert.assertFalse( Hibernate.isPropertyInitialized( boss, "superior" ) );
assertTrue( Hibernate.isPropertyInitialized( boss, "superior" ) );
assertTrue( Hibernate.isInitialized( boss.superior ) );
assertThat( boss.superior, not( instanceOf( HibernateProxy.class ) ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( boss, "subordinates" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( leader, "superior" ) );
assertTrue( Hibernate.isPropertyInitialized( leader, "superior" ) );
assertTrue( Hibernate.isInitialized( leader.superior ) );
assertThat( leader.superior, not( instanceOf( HibernateProxy.class ) ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( leader, "subordinates" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "superior" ) );
assertTrue( Hibernate.isPropertyInitialized( member, "superior" ) );
assertTrue( Hibernate.isInitialized( member.superior ) );
assertThat( member.superior, not( instanceOf( HibernateProxy.class ) ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "subordinates" ) );
}
);
@ -108,37 +104,34 @@ public class UninitializedAssociationsInCacheTest extends BaseCoreFunctionalTest
assertEquals( 3, regionStatistics.getMissCount() );
assertEquals( 3, regionStatistics.getPutCount() );
TransactionUtil.doInHibernate(
this::sessionFactory, (s) -> {
final Employee boss = s.find( Employee.class, bossId.get() );
final Employee leader = s.find( Employee.class, teamleaderId.get() );
final Employee member = s.find( Employee.class, teammemberId.get() );
Assert.assertFalse( Hibernate.isPropertyInitialized( boss, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( boss, "subordinates" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "subordinates" ) );
Assert.assertNull( boss.superior );
inTransaction(
(s) -> {
final Employee boss = s.find( Employee.class, 1 );
final Employee leader = s.find( Employee.class, 2 );
final Employee member = s.find( Employee.class, 3 );
Assert.assertTrue( Hibernate.isPropertyInitialized( boss, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( boss, "subordinates" ) );
Assert.assertEquals( leader, boss.subordinates.iterator().next() );
Assert.assertTrue( Hibernate.isPropertyInitialized( boss, "subordinates" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( leader, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( leader, "subordinates" ) );
Assert.assertEquals( boss, leader.superior );
Assert.assertTrue( Hibernate.isPropertyInitialized( leader, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( leader, "subordinates" ) );
Assert.assertEquals( member, leader.subordinates.iterator().next() );
Assert.assertTrue( Hibernate.isPropertyInitialized( leader, "subordinates" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "subordinates" ) );
Assert.assertEquals( leader, member.superior );
Assert.assertTrue( Hibernate.isPropertyInitialized( member, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "subordinates" ) );
Assert.assertTrue( member.subordinates.isEmpty() );
Assert.assertTrue( Hibernate.isPropertyInitialized( member, "subordinates" ) );
Assert.assertNull( boss.superior );
assertTrue( Hibernate.isPropertyInitialized( boss, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( boss, "subordinates" ) );
Assert.assertEquals( leader, boss.subordinates.iterator().next() );
assertTrue( Hibernate.isPropertyInitialized( boss, "subordinates" ) );
Assert.assertTrue( Hibernate.isPropertyInitialized( leader, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( leader, "subordinates" ) );
Assert.assertEquals( boss, leader.superior );
Assert.assertEquals( member, leader.subordinates.iterator().next() );
assertTrue( Hibernate.isPropertyInitialized( leader, "subordinates" ) );
Assert.assertTrue( Hibernate.isPropertyInitialized( member, "superior" ) );
Assert.assertFalse( Hibernate.isPropertyInitialized( member, "subordinates" ) );
Assert.assertEquals( leader, member.superior );
assertTrue( member.subordinates.isEmpty() );
assertTrue( Hibernate.isPropertyInitialized( member, "subordinates" ) );
}
);
@ -152,10 +145,8 @@ public class UninitializedAssociationsInCacheTest extends BaseCoreFunctionalTest
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "Employee")
private static class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SUPERIOR")
@ -167,5 +158,18 @@ public class UninitializedAssociationsInCacheTest extends BaseCoreFunctionalTest
@Basic
String regularString;
private Employee() {
}
public Employee(Integer id, String regularString) {
this.id = id;
this.regularString = regularString;
}
@Override
public String toString() {
return String.format( "Employee( %s, %s )", id, regularString ) + "@" + System.identityHashCode( this );
}
}
}

View File

@ -23,7 +23,6 @@ import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
@ -32,9 +31,10 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertFalse;
import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
@ -49,10 +49,6 @@ import static org.junit.Assert.assertTrue;
EnhancerTestContext.class,
BidirectionalLazyGroupsTest.NoDirtyCheckEnhancementContext.class
})
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public class BidirectionalLazyGroupsTest extends BaseCoreFunctionalTestCase {
public Class<?>[] getAnnotatedClasses() {
@ -61,11 +57,9 @@ public class BidirectionalLazyGroupsTest extends BaseCoreFunctionalTestCase {
@Test
public void testRemoveCollectionOwnerNoCascade() {
doInHibernate(
this::sessionFactory, session -> {
Employer employer = new Employer( "RedHat" );
inTransaction(
(session) -> {
final Employer employer = new Employer( "RedHat" );
session.persist( employer );
employer.addEmployee( new Employee( "Jack" ) );
employer.addEmployee( new Employee( "Jill" ) );
@ -76,20 +70,21 @@ public class BidirectionalLazyGroupsTest extends BaseCoreFunctionalTestCase {
}
);
doInHibernate(
this::sessionFactory, session -> {
Employer employer = session.createQuery( "from Employer e", Employer.class ).getSingleResult();
inTransaction(
(session) -> {
final Employer employer = session.createQuery( "from Employer e", Employer.class ).getSingleResult();
session.remove( employer );
for ( Employee employee : employer.getEmployees() ) {
assertFalse( Hibernate.isPropertyInitialized( employee, "employer") );
assertTrue( Hibernate.isPropertyInitialized( employee, "employer") );
assertThat( employee.getEmployer(), CoreMatchers.sameInstance( employer ) );
session.remove( employee );
assertTrue( Hibernate.isPropertyInitialized( employee, "employer" ) );
}
}
);
doInHibernate(
this::sessionFactory, session -> {
inTransaction(
(session) -> {
assertNull( session.find( Employer.class, "RedHat" ) );
assertNull( session.createQuery( "from Employee e", Employee.class ).uniqueResult() );
}
@ -200,7 +195,7 @@ public class BidirectionalLazyGroupsTest extends BaseCoreFunctionalTestCase {
return true;
}
else if ( o instanceof Employee ) {
Employee other = Employee.class.cast( o );
Employee other = (Employee) o;
if ( name != null ) {
return getName().equals( other.getName() );
}

View File

@ -6,22 +6,8 @@
*/
package org.hibernate.test.bytecode.enhancement.lazy.group;
import org.hibernate.annotations.LazyGroup;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
@ -32,26 +18,39 @@ import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.List;
import static org.hibernate.testing.bytecode.enhancement.EnhancerTestUtils.getFieldByReflection;
import org.hibernate.Hibernate;
import org.hibernate.annotations.LazyGroup;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
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.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
*/
@TestForIssue( jiraKey = "HHH-11155" )
@RunWith( BytecodeEnhancerRunner.class )
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public class LazyGroupTest extends BaseCoreFunctionalTestCase {
private SQLStatementInterceptor sqlInterceptor;
@Override
public Class<?>[] getAnnotatedClasses() {
@ -62,6 +61,7 @@ public class LazyGroupTest extends BaseCoreFunctionalTestCase {
protected void configure(Configuration configuration) {
configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
configuration.setProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
sqlInterceptor = new SQLStatementInterceptor( configuration );
}
@Before
@ -93,39 +93,50 @@ public class LazyGroupTest extends BaseCoreFunctionalTestCase {
@Test
@TestForIssue( jiraKey = "HHH-10267" )
public void testAccess() {
doInHibernate( this::sessionFactory, s -> {
Child c1 = (Child) s.createQuery( "from Child c where c.name = :name" ).setParameter( "name", "steve" ).uniqueResult();
sqlInterceptor.clear();
// verify the expected initial loaded state
assertLoaded( c1, "name" );
assertNotLoaded( c1, "nickName" );
assertNotLoaded( c1, "parent" );
assertNotLoaded( c1, "alternateParent" );
inTransaction(
(s) -> {
final Child c1 = s.createQuery( "from Child c where c.name = :name", Child.class )
.setParameter( "name", "steve" )
.uniqueResult();
// Now lets access nickName which ought to initialize nickName and parent, but not alternateParent
c1.getNickName();
assertLoaded( c1, "nickName" );
assertLoaded( c1, "parent" );
assertNotLoaded( c1, "alternateParent" );
assertEquals( "Hibernate", c1.parent.nombre );
assertFalse( c1.parent instanceof HibernateProxy );
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
Child c2 = (Child) s.createQuery( "from Child c where c.name = :name" ).setParameter( "name", "sally" ).uniqueResult();
assertTrue( Hibernate.isPropertyInitialized( c1, "name" ) );
// verify the expected initial loaded state
assertLoaded( c2, "name" );
assertNotLoaded( c2, "nickName" );
assertNotLoaded( c2, "parent" );
assertNotLoaded( c2, "alternateParent" );
assertFalse( Hibernate.isPropertyInitialized( c1, "nickName" ) );
// Now lets access alternateParent which ought to initialize alternateParent and nothing else
c2.getAlternateParent();
assertNotLoaded( c2, "nickName" );
assertNotLoaded( c2, "parent" );
assertLoaded( c2, "alternateParent" );
assertEquals( "Hibernate", c2.alternateParent.nombre );
assertFalse( c2.alternateParent instanceof HibernateProxy );
} );
// parent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "parent" ) );
assertThat( c1.getParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getParent() ) );
// alternateParent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "alternateParent" ) );
assertThat( c1.getAlternateParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getAlternateParent() ) );
// Now lets access nickName which ought to initialize nickName
c1.getNickName();
assertThat( sqlInterceptor.getQueryCount(), is( 2 ) );
assertTrue( Hibernate.isPropertyInitialized( c1, "nickName" ) );
// parent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "parent" ) );
assertThat( c1.getParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getParent() ) );
// alternateParent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "alternateParent" ) );
assertThat( c1.getAlternateParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getAlternateParent() ) );
sqlInterceptor.clear();
}
);
}
@Test
@ -134,64 +145,66 @@ public class LazyGroupTest extends BaseCoreFunctionalTestCase {
Parent p1New = new Parent();
p1New.nombre = "p1New";
doInHibernate( this::sessionFactory, s -> {
Child c1 = (Child) s.createQuery( "from Child c where c.name = :name" ).setParameter( "name", "steve" ).uniqueResult();
inTransaction(
(s) -> {
sqlInterceptor.clear();
// verify the expected initial loaded state
assertLoaded( c1, "name" );
assertNotLoaded( c1, "nickName" );
assertNotLoaded( c1, "parent" );
assertNotLoaded( c1, "alternateParent" );
final Child c1 = s.createQuery( "from Child c where c.name = :name", Child.class )
.setParameter( "name", "steve" )
.uniqueResult();
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
// Now lets update nickName which ought to initialize nickName and parent, but not alternateParent
c1.nickName = "new nickName";
assertLoaded( c1, "nickName" );
assertNotLoaded( c1, "parent" );
assertNotLoaded( c1, "alternateParent" );
assertEquals( "Hibernate", c1.parent.nombre );
assertFalse( c1.parent instanceof HibernateProxy );
assertTrue( Hibernate.isPropertyInitialized( c1, "name" ) );
// Now update c1.parent
c1.parent.children.remove( c1 );
c1.parent = p1New;
p1New.children.add( c1 );
} );
assertFalse( Hibernate.isPropertyInitialized( c1, "nickName" ) );
// parent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "parent" ) );
assertThat( c1.getParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getParent() ) );
// alternateParent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "alternateParent" ) );
assertThat( c1.getAlternateParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getAlternateParent() ) );
// Now lets update nickName
c1.nickName = "new nickName";
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
assertTrue( Hibernate.isPropertyInitialized( c1, "name" ) );
assertTrue( Hibernate.isPropertyInitialized( c1, "nickName" ) );
// parent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "parent" ) );
assertThat( c1.getParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getParent() ) );
// alternateParent should be an uninitialized enhanced-proxy
assertTrue( Hibernate.isPropertyInitialized( c1, "alternateParent" ) );
assertThat( c1.getAlternateParent(), not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isInitialized( c1.getAlternateParent() ) );
// Now update c1.parent
c1.parent.children.remove( c1 );
c1.parent = p1New;
p1New.children.add( c1 );
}
);
// verify updates
doInHibernate( this::sessionFactory, s -> {
Child c1 = (Child) s.createQuery( "from Child c where c.name = :name" ).setParameter( "name", "steve" ).uniqueResult();
assertEquals( "new nickName", c1.getNickName() );
assertEquals( "p1New", c1.parent.nombre );
assertFalse( c1.parent instanceof HibernateProxy );
} );
inTransaction(
(s) -> {
final Child c1 = s.createQuery( "from Child c where c.name = :name", Child.class )
.setParameter( "name", "steve" )
.uniqueResult();
doInHibernate( this::sessionFactory, s -> {
Child c2 = (Child) s.createQuery( "from Child c where c.name = :name" ).setParameter( "name", "sally" ).uniqueResult();
// verify the expected initial loaded state
assertLoaded( c2, "name" );
assertNotLoaded( c2, "nickName" );
assertNotLoaded( c2, "parent" );
assertNotLoaded( c2, "alternateParent" );
// Now lets access and update alternateParent which ought to initialize alternateParent and nothing else
Parent p1 = c2.getAlternateParent();
c2.alternateParent = p1New;
assertNotLoaded( c2, "nickName" );
assertNotLoaded( c2, "parent" );
assertLoaded( c2, "alternateParent" );
assertEquals( "p1New", c2.getAlternateParent().nombre );
assertFalse( c2.getAlternateParent() instanceof HibernateProxy );
p1.alternateChildren.remove( c2 );
p1New.alternateChildren.add( c2 );
} );
// verify update
doInHibernate( this::sessionFactory, s -> {
Child c2 = (Child) s.createQuery( "from Child c where c.name = :name" ).setParameter( "name", "sally" ).uniqueResult();
assertEquals( "p1New", c2.getAlternateParent().nombre );
} );
assertThat( c1.getNickName(), is( "new nickName" ) );
assertThat( c1.parent.nombre, is( "p1New" ) );
}
);
}
@After
@ -204,18 +217,6 @@ public class LazyGroupTest extends BaseCoreFunctionalTestCase {
// --- //
private void assertLoaded(Object owner, String name) {
// NOTE we assume null == not-loaded
Object fieldByReflection = getFieldByReflection( owner, name );
assertNotNull( "Expecting field '" + name + "' to be loaded, but it was not", fieldByReflection );
}
private void assertNotLoaded(Object owner, String name) {
// NOTE we assume null == not-loaded
Object fieldByReflection = getFieldByReflection( owner, name );
assertNull( "Expecting field '" + name + "' to be not loaded, but it was", fieldByReflection );
}
// --- //
@Entity( name = "Parent" )
@ -271,6 +272,10 @@ public class LazyGroupTest extends BaseCoreFunctionalTestCase {
this.nickName = nickName;
}
public Parent getParent() {
return parent;
}
Parent getAlternateParent() {
return alternateParent;
}

View File

@ -6,10 +6,6 @@
*/
package org.hibernate.test.bytecode.enhancement.lazy.notfound;
/**
* @author Gail Badner
*/
import javax.persistence.CascadeType;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
@ -26,7 +22,6 @@ import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -37,6 +32,9 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
/**
* @author Gail Badner
*/
@TestForIssue( jiraKey = "HHH-12226")
@RunWith( BytecodeEnhancerRunner.class )
public class LazyNotFoundManyToOneNonUpdatableNonInsertableTest extends BaseCoreFunctionalTestCase {
@ -51,10 +49,6 @@ public class LazyNotFoundManyToOneNonUpdatableNonInsertableTest extends BaseCore
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void test() {
doInHibernate(
this::sessionFactory, session -> {

View File

@ -28,6 +28,7 @@ import org.hibernate.annotations.NotFoundAction;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@ -6,10 +6,6 @@
*/
package org.hibernate.test.bytecode.enhancement.lazy.notfound;
/**
* @author Gail Badner
*/
import javax.persistence.CascadeType;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
@ -28,23 +24,29 @@ import org.hibernate.annotations.NotFoundAction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.is;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
/**
* @author Gail Badner
*/
@TestForIssue( jiraKey = "HHH-12226")
@RunWith( BytecodeEnhancerRunner.class )
public class LazyNotFoundOneToOneTest extends BaseCoreFunctionalTestCase {
private static int ID = 1;
private SQLStatementInterceptor sqlInterceptor;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
@ -56,14 +58,10 @@ public class LazyNotFoundOneToOneTest extends BaseCoreFunctionalTestCase {
@Override
protected void configure(Configuration configuration) {
super.configure(configuration);
configuration.setProperty( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
sqlInterceptor = new SQLStatementInterceptor( configuration );
}
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void test() {
doInHibernate(
this::sessionFactory, session -> {
@ -82,10 +80,15 @@ public class LazyNotFoundOneToOneTest extends BaseCoreFunctionalTestCase {
}
);
sqlInterceptor.clear();
doInHibernate(
this::sessionFactory, session -> {
User user = session.find( User.class, ID );
assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
assertFalse( Hibernate.isPropertyInitialized( user, "lazy" ) );
assertNull( user.getLazy() );
}
);

View File

@ -200,7 +200,6 @@ public class FetchGraphTest extends BaseNonConfigCoreFunctionalTestCase {
}
@Test
@FailureExpected( jiraKey = "HHH-13658")
public void testRandomAccess() {
final StatisticsImplementor stats = sessionFactory().getStatistics();
stats.clear();

View File

@ -10,24 +10,28 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
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.FailureExpected;
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.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
/**
@ -39,10 +43,25 @@ import static org.junit.Assert.assertEquals;
@EnhancementOptions( lazyLoading = true )
public class LazyGroupWithInheritanceTest extends BaseNonConfigCoreFunctionalTestCase {
@Test
@FailureExpected(
jiraKey = "HHH-13658",
message = "Assertions specific to enhanced lazy loading but disallowing enhanced proxies, which is no longer valid"
)
public void loadEntityWithAssociationToAbstract() {
final Statistics stats = sessionFactory().getStatistics();
stats.clear();
inTransaction(
(session) -> {
final Order loaded = session.byId( Order.class ).load( 1 );
assert Hibernate.isPropertyInitialized( loaded, "customer" );
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
assertThat( loaded, instanceOf( PersistentAttributeInterceptable.class ) );
final PersistentAttributeInterceptor interceptor = ((PersistentAttributeInterceptable) loaded).$$_hibernate_getInterceptor();
assertThat( interceptor, instanceOf( BytecodeLazyAttributeInterceptor.class ) );
final BytecodeLazyAttributeInterceptor interceptor1 = (BytecodeLazyAttributeInterceptor) interceptor;
}
);
}
@Test
public void queryEntityWithAssociationToAbstract() {
final Statistics stats = sessionFactory().getStatistics();
stats.clear();
@ -61,8 +80,8 @@ public class LazyGroupWithInheritanceTest extends BaseNonConfigCoreFunctionalTes
// 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 );
expectedQueryCount.set( 1 );
//expectedQueryCount.set( 4 );
assertEquals( expectedQueryCount.get(), stats.getPrepareStatementCount() );
for ( Order order : orders ) {

View File

@ -84,7 +84,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

@ -6,6 +6,7 @@
*/
package org.hibernate.test.mapping.lazytoone;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@ -24,6 +25,8 @@ import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
public class Flight {
@Id
private Integer id;
@Column( name = "flight_number" )
private String number;
@ManyToOne( fetch = LAZY )

View File

@ -0,0 +1,196 @@
/*
* 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.mapping.lazytoone;
import java.math.BigDecimal;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Fetch;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
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.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.FetchMode.JOIN;
/**
* Test for lazy uni-directional to-one (with JOIN fetching) when enhanced proxies are allowed
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class JoinFetchedManyToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( Order.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final Order order = new Order( 1, customer, BigDecimal.ONE );
session.persist( order );
}
);
sqlStatementInterceptor.clear();
final EntityPersister orderDescriptor = sessionFactory().getMetamodel().entityPersister( Order.class );
final BytecodeEnhancementMetadata orderEnhancementMetadata = orderDescriptor.getBytecodeEnhancementMetadata();
assertThat( orderEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
final Order order = session.byId( Order.class ).getReference( 1 );
// we should have an uninitialized enhanced proxy - therefore no SQL statements should have been executed
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
final BytecodeLazyAttributeInterceptor initialInterceptor = orderEnhancementMetadata.extractLazyInterceptor( order );
assertThat( initialInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// access the id - should do nothing with db
order.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat( initialInterceptor, sameInstance( orderEnhancementMetadata.extractLazyInterceptor( order ) ) );
// this should trigger loading the entity's base state which includes customer.
// and since customer is defined for join fetch we
order.getAmount();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
assertThat( initialInterceptor, not( sameInstance( orderEnhancementMetadata.extractLazyInterceptor( order ) ) ) );
// should not trigger a load - Order's base fetch state includes customer
final Customer customer = order.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
// and since Order#customer is mapped for JOIN fetching, Customer should be fully initialized as well
assertThat( Hibernate.isInitialized( customer ), is( true ) );
// just as above, accessing id should trigger no loads
customer.getId();
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "Order")
@Table( name = "`order`")
public static class Order {
@Id
private Integer id;
@ManyToOne( fetch = LAZY )
@Fetch( JOIN )
//we want it to behave as if...
//@LazyToOne( NO_PROXY )
private Customer customer;
private BigDecimal amount;
public Order() {
}
public Order(Integer id, Customer customer, BigDecimal amount) {
this.id = id;
this.customer = customer;
this.amount = amount;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
}

View File

@ -0,0 +1,216 @@
/*
* 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.mapping.lazytoone;
import java.math.BigDecimal;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
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.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Test for lazy uni-directional to-one (with SELECT fetching) when enhanced proxies are allowed
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class ManyToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( Order.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister orderDescriptor = sessionFactory().getMetamodel().entityPersister( Order.class );
final BytecodeEnhancementMetadata orderEnhancementMetadata = orderDescriptor.getBytecodeEnhancementMetadata();
assertThat( orderEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
final Order order = session.byId( Order.class ).getReference( 1 );
// we should have just the uninitialized proxy of the owner - and
// therefore no SQL statements should have been executed
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
final BytecodeLazyAttributeInterceptor initialInterceptor = orderEnhancementMetadata.extractLazyInterceptor( order );
assertThat( initialInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// access the id - should do nothing with db
order.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat( initialInterceptor, sameInstance( orderEnhancementMetadata.extractLazyInterceptor( order ) ) );
// this should trigger loading the entity's base state
order.getAmount();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor interceptor = orderEnhancementMetadata.extractLazyInterceptor( order );
assertThat( initialInterceptor, not( sameInstance( interceptor ) ) );
assertThat( interceptor, instanceOf( LazyAttributeLoadingInterceptor.class ) );
final LazyAttributeLoadingInterceptor attrInterceptor = (LazyAttributeLoadingInterceptor) interceptor;
assertThat( attrInterceptor.hasAnyUninitializedAttributes(), is( false ) );
// should not trigger a load and the `customer` reference should be an uninitialized enhanced proxy
final Customer customer = order.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor initialCustomerInterceptor = customerEnhancementMetadata.extractLazyInterceptor( customer );
assertThat( initialCustomerInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// just as above, accessing id should trigger no loads
customer.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
assertThat( initialCustomerInterceptor, sameInstance( customerEnhancementMetadata.extractLazyInterceptor( customer ) ) );
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
assertThat( customerEnhancementMetadata.extractLazyInterceptor( customer ), instanceOf( LazyAttributeLoadingInterceptor.class ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final Order order = new Order( 1, customer, BigDecimal.ONE );
session.persist( order );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Order" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "Order")
@Table( name = "`order`")
public static class Order {
@Id
private Integer id;
@ManyToOne( fetch = LAZY )
//we want it to behave as if...
//@LazyToOne( NO_PROXY )
private Customer customer;
private BigDecimal amount;
public Order() {
}
public Order(Integer id, Customer customer, BigDecimal amount) {
this.id = id;
this.customer = customer;
this.amount = amount;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.mapping.lazytoone;
import java.math.BigDecimal;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
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.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import static javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
/**
* Baseline test for uni-directional to-one, using an explicit @LazyToOne(NO_PROXY)
*
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class ManyToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( Order.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final Order order = new Order( 1, customer, BigDecimal.ONE );
session.persist( order );
}
);
sqlStatementInterceptor.clear();
final EntityPersister orderDescriptor = sessionFactory().getMetamodel().entityPersister( Order.class );
final BytecodeEnhancementMetadata orderEnhancementMetadata = orderDescriptor.getBytecodeEnhancementMetadata();
assertThat( orderEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
final Order order = session.byId( Order.class ).getReference( 1 );
// we should have just the uninitialized proxy of the owner - and
// therefore no SQL statements should have been executed
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
final BytecodeLazyAttributeInterceptor initialInterceptor = orderEnhancementMetadata.extractLazyInterceptor( order );
assertThat( initialInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// access the id - should do nothing with db
order.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat( initialInterceptor, sameInstance( orderEnhancementMetadata.extractLazyInterceptor( order ) ) );
// this should trigger loading the entity's base state
order.getAmount();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor interceptor = orderEnhancementMetadata.extractLazyInterceptor( order );
assertThat( initialInterceptor, not( sameInstance( interceptor ) ) );
assertThat( interceptor, instanceOf( LazyAttributeLoadingInterceptor.class ) );
final LazyAttributeLoadingInterceptor attrInterceptor = (LazyAttributeLoadingInterceptor) interceptor;
assertThat( attrInterceptor.hasAnyUninitializedAttributes(), is( false ) );
// should not trigger a load and the `customer` reference should be an uninitialized enhanced proxy
final Customer customer = order.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor initialCustomerInterceptor = customerEnhancementMetadata.extractLazyInterceptor( customer );
assertThat( initialCustomerInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// just as above, accessing id should trigger no loads
customer.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
assertThat( initialCustomerInterceptor, sameInstance( customerEnhancementMetadata.extractLazyInterceptor( customer ) ) );
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
assertThat( customerEnhancementMetadata.extractLazyInterceptor( customer ), instanceOf( LazyAttributeLoadingInterceptor.class ) );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Order" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "Order")
@Table( name = "`order`")
public static class Order {
@Id
private Integer id;
@ManyToOne( fetch = LAZY )
@LazyToOne( NO_PROXY )
private Customer customer;
private BigDecimal amount;
public Order() {
}
public Order(Integer id, Customer customer, BigDecimal amount) {
this.id = id;
this.customer = customer;
this.amount = amount;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
}

View File

@ -0,0 +1,227 @@
/*
* 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.mapping.lazytoone.mappedby;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
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.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class InverseToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( SupplementalInfo.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
// Get a reference to the SupplementalInfo we created
final SupplementalInfo supplementalInfo = session.byId( SupplementalInfo.class ).getReference( 1 );
// 1) we should have just the uninitialized SupplementalInfo enhanced proxy
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
final BytecodeLazyAttributeInterceptor initialInterceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// (2) Access the SupplementalInfo's id value - should trigger no SQL
supplementalInfo.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat( initialInterceptor, sameInstance( supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo ) ) );
// 3) Access SupplementalInfo's `something` state
// - should trigger loading the "base group" state, which only include `something`.
// NOTE: `customer` is not part of this lazy group because we do not know the
// Customer PK from this side
supplementalInfo.getSomething();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor interceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, not( sameInstance( interceptor ) ) );
assertThat( interceptor, instanceOf( LazyAttributeLoadingInterceptor.class ) );
// 4) Access SupplementalInfo's `customer` state
// - should trigger load from Customer table, by FK
final Customer customer = supplementalInfo.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
// just as above, accessing id should trigger no loads
customer.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Customer" ).executeUpdate();
session.createQuery( "delete SupplementalInfo" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
@OneToOne( fetch = LAZY )
private SupplementalInfo supplementalInfo;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SupplementalInfo getSupplementalInfo() {
return supplementalInfo;
}
public void setSupplementalInfo(SupplementalInfo supplementalInfo) {
this.supplementalInfo = supplementalInfo;
}
}
@Entity( name = "SupplementalInfo" )
@Table( name = "supplemental" )
public static class SupplementalInfo {
@Id
private Integer id;
@OneToOne( fetch = LAZY, mappedBy = "supplementalInfo", optional = false )
// @LazyToOne( value = NO_PROXY )
private Customer customer;
private String something;
public SupplementalInfo() {
}
public SupplementalInfo(Integer id, Customer customer, String something) {
this.id = id;
this.customer = customer;
this.something = something;
customer.setSupplementalInfo( this );
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}

View File

@ -0,0 +1,242 @@
/*
* 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.mapping.lazytoone.mappedby;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import static javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
/**
* Baseline test for inverse (mappedBy) to-one, using an explicit @LazyToOne(NO_PROXY)
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class InverseToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( SupplementalInfo.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
// 1) Get a reference to the SupplementalInfo we created
// - at that point there should be no SQL executed
// 2) Access the SupplementalInfo's id value
// - should trigger no SQL
// 3) Access SupplementalInfo's `something` state
// - should trigger loading the "base group" state, which only include `something`.
// NOTE: `customer` is not part of this lazy group because we do not know the
// Customer PK from this side
// 4) Access SupplementalInfo's `customer` state
// - should trigger load from Customer table
final SupplementalInfo supplementalInfo = session.byId( SupplementalInfo.class ).getReference( 1 );
// we should have just the uninitialized SupplementalInfo proxy
// - therefore no SQL statements should have been executed
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat(
supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo ),
instanceOf( EnhancementAsProxyLazinessInterceptor.class )
);
// access the id - should do nothing with db
supplementalInfo.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat(
supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo ),
instanceOf( EnhancementAsProxyLazinessInterceptor.class )
);
// this should trigger loading the entity's base state
supplementalInfo.getSomething();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
assertThat(
supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo ),
instanceOf( LazyAttributeLoadingInterceptor.class )
);
// because we do not know the customer FK from the SupplementalInfo side
// it is considered part of SupplementalInfo's non-base state.
//
// here we access customer which triggers a load from customer table
final Customer customer = supplementalInfo.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
assertThat(
customerEnhancementMetadata.extractLazyInterceptor( customer ),
instanceOf( LazyAttributeLoadingInterceptor.class )
);
// just as above, accessing id should trigger no loads
customer.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Customer" ).executeUpdate();
session.createQuery( "delete SupplementalInfo" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
@OneToOne( fetch = LAZY )
private SupplementalInfo supplementalInfo;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SupplementalInfo getSupplementalInfo() {
return supplementalInfo;
}
public void setSupplementalInfo(SupplementalInfo supplementalInfo) {
this.supplementalInfo = supplementalInfo;
}
}
@Entity( name = "SupplementalInfo" )
@Table( name = "supplemental" )
public static class SupplementalInfo {
@Id
private Integer id;
@OneToOne( fetch = LAZY, mappedBy = "supplementalInfo", optional = false )
@LazyToOne( value = NO_PROXY )
private Customer customer;
private String something;
public SupplementalInfo() {
}
public SupplementalInfo(Integer id, Customer customer, String something) {
this.id = id;
this.customer = customer;
this.something = something;
customer.setSupplementalInfo( this );
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}

View File

@ -0,0 +1,225 @@
/*
* 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.mapping.lazytoone.mappedby;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
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.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.FetchMode.JOIN;
/**
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class JoinFetchedInverseToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( SupplementalInfo.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
// Get a reference to the SupplementalInfo we created
final SupplementalInfo supplementalInfo = session.byId( SupplementalInfo.class ).getReference( 1 );
// 1) we should have just the uninitialized SupplementalInfo enhanced proxy
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
final BytecodeLazyAttributeInterceptor initialInterceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// 2) Access the SupplementalInfo's id value - should trigger no SQL
supplementalInfo.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat( initialInterceptor, sameInstance( supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo ) ) );
// 3) Access SupplementalInfo's `something` state
// - should trigger loading the "base group" state
// - customer should be join fetched as part of loading this base state
supplementalInfo.getSomething();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor interceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, not( sameInstance( interceptor ) ) );
assertThat( interceptor, instanceOf( LazyAttributeLoadingInterceptor.class ) );
// 4) Access SupplementalInfo's `customer` state
final Customer customer = supplementalInfo.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
customer.getId();
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Customer" ).executeUpdate();
session.createQuery( "delete SupplementalInfo" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
@OneToOne( fetch = LAZY )
private SupplementalInfo supplementalInfo;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SupplementalInfo getSupplementalInfo() {
return supplementalInfo;
}
public void setSupplementalInfo(SupplementalInfo supplementalInfo) {
this.supplementalInfo = supplementalInfo;
}
}
@Entity( name = "SupplementalInfo" )
@Table( name = "supplemental" )
public static class SupplementalInfo {
@Id
private Integer id;
@OneToOne( fetch = LAZY, mappedBy = "supplementalInfo", optional = false )
// @LazyToOne( value = NO_PROXY )
@Fetch( JOIN )
private Customer customer;
private String something;
public SupplementalInfo() {
}
public SupplementalInfo(Integer id, Customer customer, String something) {
this.id = id;
this.customer = customer;
this.something = something;
customer.setSupplementalInfo( this );
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}

View File

@ -0,0 +1,197 @@
/*
* 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.mapping.lazytoone.onetoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.FetchMode.JOIN;
/**
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class JoinFetchedOneToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( SupplementalInfo.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
final SupplementalInfo supplementalInfo = session.byId( SupplementalInfo.class ).getReference( 1 );
// we should have just the uninitialized SupplementalInfo proxy
// - therefore no SQL statements should have been executed
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
// access the id - should do nothing with db
supplementalInfo.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
// this should trigger loading the entity's base state which should include join fetching Customer
supplementalInfo.getSomething();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
// should not trigger a load and the `customer` reference should be an uninitialized enhanced proxy
final Customer customer = supplementalInfo.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
// just as above, accessing id should trigger no loads
customer.getId();
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete SupplementalInfo" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "SupplementalInfo" )
@Table( name = "supplemental" )
public static class SupplementalInfo {
@Id
private Integer id;
@OneToOne( fetch = LAZY, optional = false )
@Fetch( JOIN )
//@LazyToOne( value = NO_PROXY )
private Customer customer;
private String something;
public SupplementalInfo() {
}
public SupplementalInfo(Integer id, Customer customer, String something) {
this.id = id;
this.customer = customer;
this.something = something;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}

View File

@ -0,0 +1,216 @@
/*
* 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.mapping.lazytoone.onetoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
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.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class OneToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( SupplementalInfo.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
final SupplementalInfo supplementalInfo = session.byId( SupplementalInfo.class ).getReference( 1 );
// we should have just the uninitialized SupplementalInfo proxy
// - therefore no SQL statements should have been executed
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
final BytecodeLazyAttributeInterceptor initialInterceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// access the id - should do nothing with db
supplementalInfo.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat( supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo ), sameInstance( initialInterceptor ) );
// this should trigger loading the entity's base state
supplementalInfo.getSomething();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor interceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, not( sameInstance( interceptor ) ) );
assertThat( interceptor, instanceOf( LazyAttributeLoadingInterceptor.class ) );
final LazyAttributeLoadingInterceptor attrInterceptor = (LazyAttributeLoadingInterceptor) interceptor;
assertThat( attrInterceptor.hasAnyUninitializedAttributes(), is( false ) );
// should not trigger a load and the `customer` reference should be an uninitialized enhanced proxy
final Customer customer = supplementalInfo.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor initialCustomerInterceptor = customerEnhancementMetadata.extractLazyInterceptor( customer );
assertThat( initialCustomerInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// just as above, accessing id should trigger no loads
customer.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
assertThat( initialCustomerInterceptor, sameInstance( customerEnhancementMetadata.extractLazyInterceptor( customer ) ) );
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
assertThat( customerEnhancementMetadata.extractLazyInterceptor( customer ), instanceOf( LazyAttributeLoadingInterceptor.class ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete SupplementalInfo" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "SupplementalInfo" )
@Table( name = "supplemental" )
public static class SupplementalInfo {
@Id
private Integer id;
@OneToOne( fetch = LAZY, optional = false )
//@LazyToOne( value = NO_PROXY )
private Customer customer;
private String something;
public SupplementalInfo() {
}
public SupplementalInfo(Integer id, Customer customer, String something) {
this.id = id;
this.customer = customer;
this.something = something;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}

View File

@ -0,0 +1,218 @@
/*
* 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.mapping.lazytoone.onetoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
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.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
/**
* Baseline test for uni-directional one-to-one, using an explicit @LazyToOne(NO_PROXY) and allowing enhanced proxies
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class OneToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( SupplementalInfo.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
final EntityPersister customerDescriptor = sessionFactory().getMetamodel().entityPersister( Customer.class );
final BytecodeEnhancementMetadata customerEnhancementMetadata = customerDescriptor.getBytecodeEnhancementMetadata();
assertThat( customerEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
inTransaction(
(session) -> {
final SupplementalInfo supplementalInfo = session.byId( SupplementalInfo.class ).getReference( 1 );
// we should have just the uninitialized SupplementalInfo proxy
// - therefore no SQL statements should have been executed
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
final BytecodeLazyAttributeInterceptor initialInterceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// access the id - should do nothing with db
supplementalInfo.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 0 ) );
assertThat( supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo ), sameInstance( initialInterceptor ) );
// this should trigger loading the entity's base state
supplementalInfo.getSomething();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor interceptor = supplementalInfoEnhancementMetadata.extractLazyInterceptor( supplementalInfo );
assertThat( initialInterceptor, not( sameInstance( interceptor ) ) );
assertThat( interceptor, instanceOf( LazyAttributeLoadingInterceptor.class ) );
final LazyAttributeLoadingInterceptor attrInterceptor = (LazyAttributeLoadingInterceptor) interceptor;
assertThat( attrInterceptor.hasAnyUninitializedAttributes(), is( false ) );
// should not trigger a load and the `customer` reference should be an uninitialized enhanced proxy
final Customer customer = supplementalInfo.getCustomer();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
final BytecodeLazyAttributeInterceptor initialCustomerInterceptor = customerEnhancementMetadata.extractLazyInterceptor( customer );
assertThat( initialCustomerInterceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) );
// just as above, accessing id should trigger no loads
customer.getId();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
assertThat( initialCustomerInterceptor, sameInstance( customerEnhancementMetadata.extractLazyInterceptor( customer ) ) );
customer.getName();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 2 ) );
assertThat( customerEnhancementMetadata.extractLazyInterceptor( customer ), instanceOf( LazyAttributeLoadingInterceptor.class ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete SupplementalInfo" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "SupplementalInfo" )
@Table( name = "supplemental" )
public static class SupplementalInfo {
@Id
private Integer id;
@OneToOne( fetch = LAZY, optional = false )
@LazyToOne( value = NO_PROXY )
private Customer customer;
private String something;
public SupplementalInfo() {
}
public SupplementalInfo(Integer id, Customer customer, String something) {
this.id = id;
this.customer = customer;
this.something = something;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}

View File

@ -0,0 +1,236 @@
/*
* 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.mapping.lazytoone.polymorphic;
import java.math.BigDecimal;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Fetch;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.FetchMode.JOIN;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class JoinFetchedPolymorphicToOneTests extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testInheritedToOneLaziness() {
inTransaction(
(session) -> {
sqlStatementInterceptor.clear();
final Order order = session.byId( Order.class ).getReference( 1 );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 0 ) );
System.out.println( "Order # " + order.getId() );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 0 ) );
System.out.println( " - amount : " + order.getAmount() );
// triggers load of base fetch state
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
final Customer customer = order.getCustomer();
// customer is part of base fetch state
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
assertTrue( Hibernate.isInitialized( customer ) );
System.out.println( " - customer : " + customer.getId() );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
customer.getName();
// customer base fetch state should also have been loaded above
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final DomesticCustomer customer = new DomesticCustomer( 1, "them", "123" );
session.persist( customer );
final Order order = new Order( 1, BigDecimal.ONE, customer );
session.persist( order );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Order" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Order.class );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( ForeignCustomer.class );
sources.addAnnotatedClass( DomesticCustomer.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Entity( name = "Order" )
@Table( name = "`order`" )
public static class Order {
@Id
private Integer id;
private BigDecimal amount;
@ManyToOne( fetch = LAZY, optional = false )
@Fetch( JOIN )
private Customer customer;
public Order() {
}
public Order(Integer id, BigDecimal amount, Customer customer) {
this.id = id;
this.amount = amount;
this.customer = customer;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
@Entity( name = "Customer" )
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
public static abstract class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity(name = "ForeignCustomer")
@Table(name = "foreign_cust")
public static class ForeignCustomer extends Customer {
private String vat;
public ForeignCustomer() {
super();
}
public ForeignCustomer(Integer id, String name, String vat) {
super( id, name );
this.vat = vat;
}
public String getVat() {
return vat;
}
public void setVat(String vat) {
this.vat = vat;
}
}
@Entity(name = "DomesticCustomer")
@Table(name = "domestic_cust")
public static class DomesticCustomer extends Customer {
private String taxId;
public DomesticCustomer() {
super();
}
public DomesticCustomer(Integer id, String name, String taxId) {
super( id, name );
this.taxId = taxId;
}
public String getTaxId() {
return taxId;
}
public void setTaxId(String taxId) {
this.taxId = taxId;
}
}
}

View File

@ -0,0 +1,264 @@
/*
* 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.mapping.lazytoone.polymorphic;
import java.math.BigDecimal;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
/**
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class PolymorphicToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testInheritedToOneLaziness() {
inTransaction(
(session) -> {
sqlStatementInterceptor.clear();
// NOTE : this test shows an edge case that does not work the way it
// should. Because we have a polymorphic to-one, we will have to
// generate a HibernateProxy for the laziness. However, the explicit
// NO_PROXY should force the proxy to be immediately initialized
// and the target returned.
//
// this is the old behavior as well - these HHH-13658 changes did not cause this
//
// its an odd edge case however and maybe not that critical. it essentially
// asks for the association to be lazy and to also be eager
//
// The assertions here are based on what *does* happen. Whether that is right/wrong
// is a different discussion
final Order order = session.byId( Order.class ).getReference( 1 );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 0 ) );
System.out.println( "Order # " + order.getId() );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 0 ) );
System.out.println( " - amount : " + order.getAmount() );
// triggers load of base fetch state
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
final Customer customer = order.getCustomer();
// this *should* be 2 - the customer should get loaded
//int expectedCount = 2;
// but it is 1 because we get back a HibernateProxy
int expectedCount = 1;
assertThat( sqlStatementInterceptor.getQueryCount(), is( expectedCount ) );
// should be true...
//assertTrue( Hibernate.isInitialized( customer ) );
// but is false
assertFalse( Hibernate.isInitialized( customer ) );
// should not be a HibernateProxy
//assertThat( customer, not( instanceOf( HibernateProxy.class ) ) );
// but is
assertThat( customer, instanceOf( HibernateProxy.class ) );
System.out.println( " - customer : " + customer.getId() );
assertThat( sqlStatementInterceptor.getQueryCount(), is( expectedCount ) );
customer.getName();
// this should not trigger SQL because the customer ought to already be initialized
// but again that is not the case
expectedCount++;
assertThat( sqlStatementInterceptor.getQueryCount(), is( expectedCount ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final DomesticCustomer customer = new DomesticCustomer( 1, "them", "123" );
session.persist( customer );
final Order order = new Order( 1, BigDecimal.ONE, customer );
session.persist( order );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Order" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Order.class );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( ForeignCustomer.class );
sources.addAnnotatedClass( DomesticCustomer.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Entity( name = "Order" )
@Table( name = "`order`" )
public static class Order {
@Id
private Integer id;
private BigDecimal amount;
@ManyToOne( fetch = LAZY, optional = false )
@LazyToOne( LazyToOneOption.NO_PROXY )
private Customer customer;
public Order() {
}
public Order(Integer id, BigDecimal amount, Customer customer) {
this.id = id;
this.amount = amount;
this.customer = customer;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
@Entity( name = "Customer" )
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
public static abstract class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity(name = "ForeignCustomer")
@Table(name = "foreign_cust")
public static class ForeignCustomer extends Customer {
private String vat;
public ForeignCustomer() {
super();
}
public ForeignCustomer(Integer id, String name, String vat) {
super( id, name );
this.vat = vat;
}
public String getVat() {
return vat;
}
public void setVat(String vat) {
this.vat = vat;
}
}
@Entity(name = "DomesticCustomer")
@Table(name = "domestic_cust")
public static class DomesticCustomer extends Customer {
private String taxId;
public DomesticCustomer() {
super();
}
public DomesticCustomer(Integer id, String name, String taxId) {
super( id, name );
this.taxId = taxId;
}
public String getTaxId() {
return taxId;
}
public void setTaxId(String taxId) {
this.taxId = taxId;
}
}
}

View File

@ -0,0 +1,235 @@
/*
* 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.mapping.lazytoone.polymorphic;
import java.math.BigDecimal;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
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 javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
/**
* @author Steve Ebersole
*/
@RunWith( BytecodeEnhancerRunner.class)
@EnhancementOptions( lazyLoading = true )
public class PolymorphicToOneImplicitOptionTests extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testInheritedToOneLaziness() {
inTransaction(
(session) -> {
sqlStatementInterceptor.clear();
final Order order = session.byId( Order.class ).getReference( 1 );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 0 ) );
System.out.println( "Order # " + order.getId() );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 0 ) );
System.out.println( " - amount : " + order.getAmount() );
// triggers load of base fetch state
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
final Customer customer = order.getCustomer();
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
// customer is part of base fetch state
assertFalse( Hibernate.isInitialized( customer ) );
assertThat( customer, instanceOf( HibernateProxy.class ) );
System.out.println( " - customer : " + customer.getId() );
assertThat( sqlStatementInterceptor.getQueryCount(), is( 1 ) );
customer.getName();
assertThat( sqlStatementInterceptor.getQueryCount(), is( 2 ) );
}
);
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final DomesticCustomer customer = new DomesticCustomer( 1, "them", "123" );
session.persist( customer );
final Order order = new Order( 1, BigDecimal.ONE, customer );
session.persist( order );
}
);
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Order" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Order.class );
sources.addAnnotatedClass( Customer.class );
sources.addAnnotatedClass( ForeignCustomer.class );
sources.addAnnotatedClass( DomesticCustomer.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
sqlStatementInterceptor = new SQLStatementInterceptor( ssrb );
}
@Entity( name = "Order" )
@Table( name = "`order`" )
public static class Order {
@Id
private Integer id;
private BigDecimal amount;
@ManyToOne( fetch = LAZY, optional = false )
private Customer customer;
public Order() {
}
public Order(Integer id, BigDecimal amount, Customer customer) {
this.id = id;
this.amount = amount;
this.customer = customer;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
@Entity( name = "Customer" )
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
public static abstract class Customer {
@Id
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity(name = "ForeignCustomer")
@Table(name = "foreign_cust")
public static class ForeignCustomer extends Customer {
private String vat;
public ForeignCustomer() {
super();
}
public ForeignCustomer(Integer id, String name, String vat) {
super( id, name );
this.vat = vat;
}
public String getVat() {
return vat;
}
public void setVat(String vat) {
this.vat = vat;
}
}
@Entity(name = "DomesticCustomer")
@Table(name = "domestic_cust")
public static class DomesticCustomer extends Customer {
private String taxId;
public DomesticCustomer() {
super();
}
public DomesticCustomer(Integer id, String name, String taxId) {
super( id, name );
this.taxId = taxId;
}
public String getTaxId() {
return taxId;
}
public void setTaxId(String taxId) {
this.taxId = taxId;
}
}
}

View File

@ -10,7 +10,9 @@ import java.util.LinkedList;
import java.util.Map;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import static org.junit.Assert.assertEquals;
@ -39,6 +41,20 @@ public class SQLStatementInterceptor {
} );
}
public SQLStatementInterceptor(StandardServiceRegistryBuilder ssrb) {
ssrb.applySetting(
AvailableSettings.STATEMENT_INSPECTOR,
(StatementInspector) sql -> {
sqlQueries.add( sql );
return sql;
}
);
}
public SQLStatementInterceptor(Configuration configuration) {
this( configuration.getProperties() );
}
public LinkedList<String> getSqlQueries() {
return sqlQueries;
}
@ -54,4 +70,8 @@ public class SQLStatementInterceptor {
public void assertExecutedCount(int expected) {
assertEquals(expected, sqlQueries.size());
}
public int getQueryCount() {
return sqlQueries.size();
}
}