Any help with this is appreciated...
I looked on the forums and found several questions regarding a one-to-one mapping and lazy loading. I have read the documentation about setting lazy="no-proxy" and adding constraint=true. After researching this and trying different mappings the one-to-one is still fetching the association eagerly.
When User is retrieved Hibernate also selects userDetail which is not the expected outcome. I added constrained=true on both sides
(I am not sure if this is necessary Hibernate is probably smart enough to determine that both sides cannot be null). The documentation states that
build time instrumentation is required for no-proxy and from reading other postings
CGLIB is in the classpath.
Using Hibernate 3.2.2
Code:
<class name="core.User" ... >
<one-to-one
name="userDetail"
class="core.userDetail"
cascade="all"
constrained="true"
lazy="no-proxy"
/>
</class>
<class name="core.UserDetail" ... >
<one-to-one
name="user"
class="core.User"
constrained="true"
/>
</class>
I looked at the code and noticed the following ...
Code:
boolean lazyAvailable = persistentClass.hasPojoRepresentation() &&
FieldInterceptionHelper.isInstrumented( persistentClass.getMappedClass() );
is executed in the EntityMetamodel constructor and FieldInterceptionHlper.isIntrumented(...) always returns false. I checked the one-to-one property and lazy is true for Property.isLazy ...
Code:
if ( value instanceof ToOne ) {
// both many-to-one and one-to-one are represented as a
// Property. EntityPersister is relying on this value to
// determine "lazy fetch groups" in terms of field-level
// interception. So we need to make sure that we return
// true here for the case of many-to-one and one-to-one
// with lazy="no-proxy"
//
// * impl note - lazy="no-proxy" currently forces both
// lazy and unwrap to be set to true. The other case we
// are extremely interested in here is that of lazy="proxy"
// where lazy is set to true, but unwrap is set to false.
// thus we use both here under the assumption that this
// return is really only ever used during persister
// construction to determine the lazy property/field fetch
// groupings. If that assertion changes then this check
// needs to change as well. Partially, this is an issue with
// the overloading of the term "lazy" here...
ToOne toOneValue = ( ToOne ) value;
return toOneValue.isLazy() && toOneValue.isUnwrapProxy();
}
return lazy;
According to the documentation one-to-one only needs build time instrumentation for lazy loading and for this all you need is to place CGLIB jars in the classpath. If this field is always false then ...
Code:
boolean lazy = prop.isLazy() && lazyAvailable;
if ( lazy ) hasLazy = true;
propertyLaziness[i] = lazy;
the variable propertyLaziness[i] is always false. This array is later used in determining the hydrated state within the AbstractEntityPersister class ...
Code:
else if ( allProperties || !laziness[i] ) {
//decide which ResultSet to get the property value from:
final boolean propertyIsDeferred = hasDeferred &&
rootPersister.isSubclassPropertyDeferred( propNames[i], propSubclassNames[i] );
if ( propertyIsDeferred && sequentialSelectEmpty ) {
values[i] = null;
}
else {
final ResultSet propertyResultSet = propertyIsDeferred ? sequentialResultSet : rs;
final String[] cols = propertyIsDeferred ? propertyColumnAliases[i] : suffixedPropertyColumns[i];
values[i] = types[i].hydrate( propertyResultSet, cols, session, object );
}
}
else {
values[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
notice if laziness is false that execution enters the else-if block skipping the else which sets values[i] to LazyPropertyInitializer.UNFETCHED_PROPERTY. The values[] is used later when hydrating the entity in the TwoPhaseLoad class ...
first the code retrieves the hydratedState
Code:
Object[] hydratedState = entityEntry.getLoadedState();
then there is a loop on the properties to resolves non-lazy properties...
Code:
Type[] types = persister.getPropertyTypes();
for ( int i = 0; i < hydratedState.length; i++ ) {
final Object value = hydratedState[i];
if ( value!=LazyPropertyInitializer.UNFETCHED_PROPERTY && value!=BackrefPropertyAccessor.UNKNOWN ) {
hydratedState[i] = types[i].resolve( value, session, entity );
}
}
the if condition says it all the fact that the value will never be LazyPropertyInitializer.UNFETCHED_PROPERTY means the property will be resolved and essentially loaded ... eagerly.