I am using Hibernate 3.2.2 against Oracle 10g and have a problem with one particular type of relationship, a one-to-one where the link is on some arbitrary (i.e. not the primary key) columns. I need to make this sort of join as there are multiple possible ways in which 2 tables can be joined together. An example follows
Code:
<class name="LocalInstrumentImpl"
table="INSTRUMENT_FOR_RESYNC"
entity-name="LocalInstrumentSearch"
proxy="LocalInstrument"
mutable="false">
.
.
.
<one-to-one name="foreignSiblingViaIsin"
entity-name="ForeignInstrumentSearch"
lazy="proxy"
fetch="select"
access="field"
property-ref="isin"
constrained="true">
<formula>isin</formula>
</one-to-one>
.
.
.
.
<query name="newLocalInstrument"
cacheable="false">
select ins
from ForeignInstrumentSearch ins
where
not exists (from LocalInstrumentSearch local where local.isin = ins.isin)
and not exists (from LocalInstrumentSearch cov where local.gedmId = ins.coverageId)
</query>
</class>
If I execute the newLocalInstrument query then I will see the following statements issued in my log demonstrating that it is eagerly fetching the foreignSiblingViaIsin property (and obviously finding nothing as we know in this instance there won't be a match).
Code:
07 Sep 2007 13:46:31,231 DEBUG [org.hibernate.SQL:393] (main:) - /* named HQL query ForeignInstrumentSearch.newLocalInstrument */ .... my query.....
07 Sep 2007 13:46:31,949 DEBUG [org.hibernate.SQL:393] (main:) - /* load CoverageInstrumentSearch */ ..... query to load by ISIN .....
I've stripped off the actual SQL generated as it's v long and not relevant to this issue.
As far as I can see in the code the association is loaded from the database and then proxied because the association is not on the primary. The offending code is called from org.hibernate.engine.TwoPhaseLoad (line 116)
Code:
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 ); // this is line 116
}
}
in this case types[i] will be an instance of org.hibernate.type.SpecialOneToOneType and resolve is provided by its base class org.hibernate.type.EntityType, the method is repeated below
Code:
public Object resolve(Object value, SessionImplementor session, Object owner) throws HibernateException {
if ( isNotEmbedded( session ) ) {
return value;
}
if ( value == null ) {
return null;
}
else {
if ( isNull( owner, session ) ) {
return null; //EARLY EXIT!
}
if ( isReferenceToPrimaryKey() ) {
return resolveIdentifier( (Serializable) value, session );
}
else {
return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
}
}
}
hence loadByUniqueKey is called, the database gets hit and then the instance is proxied.
I have a couple of questions on this;
1. is it possible to avoid an eager fetch of this type of relationship? if so, what I am doing wrong?
2. if it is not possible, is there a technical reason why this can't be implemented or is it on the feature list?
I'm dealing with ~600k records in this specific query and have two such relationships on this type hence it would be nice if I could avoid the additional 1.2m trips to the database this will trigger! At present my only solution is to replace the one-to-one with a set and assume in code that there will only ever be 1 instance in the set. This is obviously very crufty but will/should work.
Cheers
Matt