-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: Can dev explain why you decided to ignore 1to1 lazy loading?
PostPosted: Sat Jan 31, 2009 9:29 am 
Newbie

Joined: Wed Jan 28, 2009 12:00 pm
Posts: 12
I have another thread here where I am jumping through hoops to try to work around the fact that Hibernate ignores the lazy loading of 1 to 1 associated objects. To summarise, using Teneo, SET_PROXY=true, therefore all 1to1 are lazy="true" and all manyto1 are lazy="proxy"

There is advice on switching my model to many to 1 to 'work around' this behaviour. This behaviour is counter to what the user guide tells us. No where have I found an explanation as to why the Hibernate developers decided this deviation from the user guide described behaviour was required. If there is one, can someone point me to it? It will make it easier to swallow if there is a rationale behind it. Basically it looks like a bug to me.

After being faced with this problem I have had to look for alternatives. The concept of user fetch groups in JPOX looks about right. The concept of the 'how' and 'when' in Hibernate would be good too, if it worked as it says on the tin ( i.e. Chapter 19)!

Thx.

David


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 01, 2009 2:56 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
I am not a Hibernate developer but I think I know quite a lot about how Hibernate. I don't, however, understand your question. As far as I know one-to-one proxies do work in all cases were it is technically possible to implement it. Here are the 4 cases of one-to-one associations. Proxies are technically possible in 2 of the cases.

1. Using <one-to-one constrained="true">. This is a one-to-one relation on the primary key. Proxies do work because there is a database constraint that makes sure that there is an entry in the referenced table.

2. Using <one-to-one constrained="false">. This is also a one-to-one relation on the primary key. Proxies doesn't work because Hibernate must check (and at the same time load) if there is an entry in the referenced table, or if the association is null.

3. Using <many-to-one unique="true">. This is a many-to-one relation on a foreign key. Since the foreign key value must be unique it becomes a one-to-one relation. Proxies do work.

4. Using <one-to-one property-ref="...">. This is the back-direction of 3. Proxies do not work because of the same reasons in 2.

Note that if you have bi-directional associations you need to pair 1 with 2 and 3 with 4. This means that proxies will only work from one side, but not from the other.

Also note that the above doesn't include lazy="no-proxy". I have absolutely no experience with it so I am not really sure how it behaves. Since it uses byte-code manipulation I think it would be possible to proxy all four cases, but I am not sure....


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 02, 2009 4:40 am 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
The problem is described here. to summarize it:
Notice that a proxy can only be instantiated if the other end of the association exists, else yourEntity.getOneToOneEntity() would never return null even when it should be null.
On a OneToOne-relationship, where the primary key is also the foreign key, hibernate does not know, if the related entity exists, because a primary is never null. Other "toOne"-associations normally have an extra foreign-key column. If this column is null, hibernate knows that the association is null. On a OneToOne with primary-key-join-column, hibernate needs to do the select, to determine, if the association is null.

If you are using annotations, you can use "optional=false" or another JoinColumn, to tell hibernate that the association is never null.

_________________
-----------------
Need advanced help? http://www.viada.eu


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 02, 2009 10:27 am 
Newbie

Joined: Wed Jan 28, 2009 12:00 pm
Posts: 12
Hi,

Thanks, that clarifies it. But why Hibernate HAS to know that the optional end of the association exists or not is beyond me. Because I state that I want lazy loading that is the priority, I don't need to know if the optional association 1to1 entity exists or not. I'd be happy if it put a null in there until I accessed the optional end of this association. Because the alternative is an unusable OR layer in my context.

In fact is this makes Hibernate useless to me, because I have 5 of these 1to1 on the central entity so I get 5*n+1 selects when I do a search and therefore no searches are fast enough for the user.

I tried 'left join fetch' to compensate on the selects but that has some weird bug when a limit of the number of joins is reached, see other post by me for details. 18 was OK 19 was one too many. I needed to fetch to a depth of 2 from this central entity and that are over 20 associations, most of which are empty in this use case but 1to1 optional being eager forced me to try to load them all in one SQL Select.

Then I tried the getSQLQuery method, but it cannot find my class for some reason.

Looks like I am stuck. I will have to change the whole layer to JPOX and try their fetch groups, which fits my requirement closely if it works as it says in the docs.

thx.

David


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 02, 2009 10:41 am 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
double post

_________________
-----------------
Need advanced help? http://www.viada.eu


Last edited by mmerder on Mon Feb 02, 2009 10:58 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 02, 2009 10:44 am 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
double post

_________________
-----------------
Need advanced help? http://www.viada.eu


Last edited by mmerder on Mon Feb 02, 2009 10:57 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 02, 2009 10:46 am 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
olddave wrote:
Hi,
But why Hibernate HAS to know that the optional end of the association exists or not is beyond me. Because I state that I want lazy loading that is the priority, I don't need to know if the optional association 1to1 entity exists or not. I'd be happy if it put a null in there until I accessed the optional end of this association.

Actually I can not think of another way to it. I mean, lazy loading means, "placing" a proxy instead of the real object and initializing it, when it is accessed. Imagine hibernate would place a proxy without checking if the real object exists (what it currently does if you set optional=false): that would not be right, because the association should be null in reality and it would also raise an exception, when you access it.
What you say is, just placing null until you access the association. How should that work? Setting the field to null and overriding the get-method? Maybe...
Anyway, I am curious how JPOX does that. Maybe you can post it?

_________________
-----------------
Need advanced help? http://www.viada.eu


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 02, 2009 11:09 am 
Newbie

Joined: Wed Jan 28, 2009 12:00 pm
Posts: 12
I think you missed my point, in this use case I am not accessing the optional 1to1 associated entities and never will. So I am paying a significant performance penalty. In other case I will access them and I would prefer an exception if the get was accessed and the optional entity was not in existence. Or better yet override the getter.

I'll let you know if I get to the JPOX approach, in the meantime I don't want to have to rewrite all the data access layer code to use JPOX. So am trying the getSQLQuery method, although that is throwing a MappingException at the moment.

David


Top
 Profile  
 
 Post subject: Final pitch
PostPosted: Mon Feb 02, 2009 12:42 pm 
Newbie

Joined: Wed Jan 28, 2009 12:00 pm
Posts: 12
Hi.

I tried getSQLQuery thinking maybe I have more control over what is being fetched. I am even fetching the one-to-one using this method and yet Hibernates insists on fetching them all over again using 4*n+1 selects!!!!!

Here is the query

Code:
String queryString = "SELECT {inst.*},{ic.*},{de.*},{c1.*},{ul.*},{lcae.*},{issid.*},{di.*},{itype.*} " +
   "FROM instrumentdomain inst " +
   "INNER JOIN issuerclass ic ON inst.issuerclass = ic.id " +
   "LEFT OUTER JOIN denomination de ON inst.id = de.instrumentdomain " +
   "LEFT OUTER JOIN currency c1 ON c1.id = de.currency " +
+
   "LEFT OUTER JOIN underlyingcount ul ON ul.instrumentDomain = inst.id " +
   "LEFT OUTER JOIN lastcae lcae ON lcae.id = inst.id"
   "LEFT OUTER JOIN issuedata issid ON issid.id = inst.id " +
   "LEFT OUTER JOIN debtissuedata di ON di.id = inst.id " +
   "LEFT OUTER JOIN instrumenttype itype ON inst.instrumenttype = itype.id ";
... etc.
SQLQuery sqlquery = session.createSQLQuery(queryString).addEntity("inst", "InstrumentDomain")
   .addEntity("ic", "IssuerClass")
   .addEntity("de","Denomination")
   .addEntity("c1","Currency")
   .addEntity("ul","UnderlyingCount")
   .addEntity("lcae","LastCAE")
   .addEntity("issid","IssueData")
   .addEntity("di","DebtIssueData")
   .addEntity("itype","InstrumentType");
sqlquery.setCacheable(true);
sqlquery.setReadOnly(true);
...
List<InstrumentDomain> results = sqlquery.list();



Here is the result
Code:
Hibernate: SELECT inst.`id` as id1_165_0_, inst.`instrumenttype` as instrume2_165_0_, inst.`instrumentformtype` as instrume3_165_0_, inst.`tradingrestrictionstype` as tradingr4_165_0_, inst.`amountoutstanding` as amountou5_165_0_, inst.`amountoutstandingdate` as amountou6_165_0_, inst.`benchmarkstrategy` as benchmar7_165_0_, inst.`contractvaluemultiplier` as contract8_165_0_, inst.`createdate` as createdate9_165_0_, inst.`defaultclause` as default10_165_0_, inst.`firstamount` as firstam11_165_0_, inst.`firstdate` as firstdate12_165_0_, inst.`firstdealingdate` as firstde13_165_0_, inst.`firsteventvalue` as firstev14_165_0_, inst.`firstsettlementdate` as firstse15_165_0_, inst.`lastamount` as lastamount16_165_0_, inst.`lastdate` as lastdate17_165_0_, inst.`lasteventvalue` as lasteve18_165_0_, inst.`marketderivedinfperiddration` as marketd19_165_0_, inst.`marketderivedinfoperiodend` as marketd20_165_0_, inst.`marketderivedinfoperiodstart` as marketd21_165_0_, inst.`modifieddate` as modifie22_165_0_, inst.`name` as name23_165_0_, inst.`nextamount` as nextamount24_165_0_, inst.`nexteventvalue` as nexteve25_165_0_, inst.`numberholders` as numberh26_165_0_, inst.`numberholdersdatetime` as numberh27_165_0_, inst.`nmberholderspreviousdatetime` as nmberho28_165_0_, inst.`objective` as objective29_165_0_, inst.`penultimateamount` as penulti30_165_0_, inst.`penultimatedate` as penulti31_165_0_, inst.`principalpaymentend` as princip32_165_0_, inst.`principalpaymentstart` as princip33_165_0_, inst.`purchasingminimums` as purchas34_165_0_, inst.`qualifiedplans` as qualifi35_165_0_, inst.`recurrable` as recurrable36_165_0_, inst.`shortname` as shortname37_165_0_, inst.`instrumentdomain` as instrum38_165_0_, inst.`issuerclass` as issuerc39_165_0_,ic.`id` as id1_191_1_, ic.`entitystatustype` as entityst2_191_1_, ic.`replacemententity` as replacem3_191_1_, ic.`replacementtype` as replacem4_191_1_, ic.`comment` as comment5_191_1_, ic.`createdate` as createdate6_191_1_, ic.`creationdate` as creation7_191_1_, ic.`duration` as duration8_191_1_, ic.`end` as end9_191_1_, ic.`incorpdate` as incorpdate10_191_1_, ic.`modifieddate` as modifie11_191_1_, ic.`name` as name12_191_1_, ic.`parentid` as parentid13_191_1_, ic.`reviewdate` as reviewdate14_191_1_, ic.`shortname` as shortname15_191_1_, ic.`start` as start16_191_1_, ic.`ultimateparentid` as ultimat17_191_1_,de.`id` as id1_101_2_, de.`calculationtype` as calculat2_101_2_, de.`indicatorstype` as indicato3_101_2_, de.`marketcenter` as marketce4_101_2_, de.`quantitydescirptiontype` as quantity5_101_2_, de.`quantitytype` as quantity6_101_2_, de.`valuationref` as valuatio7_101_2_, de.`valuationtype` as valuatio8_101_2_, de.`crossrate` as crossrate9_101_2_, de.`currency` as currency10_101_2_, de.`instrumentdomain` as instrum11_101_2_, de.`datetime` as datetime12_101_2_, de.`denomination` as denomin13_101_2_, de.`increment` as increment14_101_2_, de.`minval` as minval15_101_2_, de.`size` as size16_101_2_, de.`debtissuedata` as debtiss17_101_2_,c1.`id` as id1_83_3_, c1.`full` as full2_83_3_, c1.`scheme` as scheme3_83_3_, c1.`short` as short4_83_3_,ul.`id` as id1_329_4_, ul.`calculationtype` as calculat2_329_4_, ul.`indicatorstype` as indicato3_329_4_, ul.`marketcenter` as marketce4_329_4_, ul.`quantitydescriptiontype` as quantity5_329_4_, ul.`quantitytype` as quantity6_329_4_, ul.`valuationref` as valuatio7_329_4_, ul.`valuationtype` as valuatio8_329_4_, ul.`unittype` as unittype9_329_4_, ul.instrumentDomain as instrum10_329_4_, ul.`datetime` as datetime11_329_4_, ul.`previousdatetime` as previou12_329_4_, ul.`rank` as rank13_329_4_, ul.`underlyingcount` as underly14_329_4_, ul.`unitsize` as unitsize15_329_4_,lcae.`id` as id1_196_5_, lcae.`caetype` as caetype2_196_5_, lcae.`datetime` as datetime3_196_5_,issid.`id` as id1_181_6_, issid.issuanceSubclass as issuance2_181_6_, issid.indexDomain as indexDom3_181_6_, issid.interestRateClass as interest4_181_6_, issid.foreignExchangeDomain as foreignE5_181_6_, issid.industrySpecificClass as industry6_181_6_, issid.economicClass as economic7_181_6_, issid.demographicClass as demograp8_181_6_, issid.`issuemarkettype` as issuemar9_181_6_, issid.`issuerref` as issuerref10_181_6_, issid.`liquidationstatustype` as liquida11_181_6_, issid.`offertype` as offertype12_181_6_, issid.`programtype` as program13_181_6_, issid.`votingrightstype` as votingr14_181_6_, issid.`accrualbasistype` as accrual15_181_6_, issid.`accrualconventiontype` as accrual16_181_6_, issid.`indicatorstype` as indicat17_181_6_, issid.`modificationtype` as modific18_181_6_, issid.`agreedrestriction` as agreedr19_181_6_, issid.`allowedindebtedness` as allowed20_181_6_, issid.`announcementdate` as announc21_181_6_, issid.`auctiondate` as auction22_181_6_, issid.`closingdate` as closing23_181_6_, issid.`createdate` as createdate24_181_6_, issid.`defaultclause` as default25_181_6_, issid.`facevalue` as facevalue26_181_6_, issid.`issuedate` as issuedate27_181_6_, issid.`marginable` as marginable28_181_6_, issid.`marginablemaxlimit` as margina29_181_6_, issid.`marginableminlimit` as margina30_181_6_, issid.`modificationdatetime` as modific31_181_6_, issid.`modifieddate` as modifie32_181_6_, issid.`negativepledge` as negativ33_181_6_, issid.`refrainfromacts` as refrain34_181_6_, issid.`settlementdate` as settlem35_181_6_, issid.`underlyingcount` as underly36_181_6_, issid.`useofproceeds` as useofpr37_181_6_, issid.`validity` as validity38_181_6_,di.`id` as id1_92_7_, di.convertible as converti2_92_7_, di.`debtindicatorstype` as debtindi3_92_7_, di.`striptype` as striptype4_92_7_, di.`strippbleinstrmentidentifier` as strippbl5_92_7_, di.`coupontype` as coupontype6_92_7_, di.`createdate` as createdate7_92_7_, di.`modifieddate` as modified8_92_7_, di.`overallotment` as overallo9_92_7_, di.`strippable` as strippable10_92_7_, di.`subscriptionduration` as subscri11_92_7_, di.`subscriptionend` as subscri12_92_7_, di.`subscriptionminval` as subscri13_92_7_, di.`subscriptionstart` as subscri14_92_7_,itype.`id` as id1_171_8_, itype.`full` as full2_171_8_, itype.`scheme` as scheme3_171_8_, itype.`short` as short4_171_8_ FROM instrumentdomain inst INNER JOIN issuerclass ic ON inst.issuerclass = ic.id LEFT OUTER JOIN denomination de ON inst.id = de.instrumentdomain LEFT OUTER JOIN currency c1 ON c1.id = de.currency LEFT OUTER JOIN underlyingcount ul ON ul.instrumentDomain = inst.id LEFT OUTER JOIN lastcae lcae ON lcae.id = inst.id LEFT OUTER JOIN issuedata issid ON issid.id = inst.id LEFT OUTER JOIN debtissuedata di ON di.id = inst.id LEFT OUTER JOIN instrumenttype itype ON inst.instrumenttype = itype.id  where itype.id in (46,48,49,150,151,185,432,438,439,443,473) and inst.name like ?
Hibernate: select issuedata0_.`id` as id1_181_0_, issuedata0_.issuanceSubclass as issuance2_181_0_, issuedata0_.indexDomain as indexDom3_181_0_, issuedata0_.interestRateClass as interest4_181_0_, issuedata0_.foreignExchangeDomain as foreignE5_181_0_, issuedata0_.industrySpecificClass as industry6_181_0_, issuedata0_.economicClass as economic7_181_0_, issuedata0_.demographicClass as demograp8_181_0_, issuedata0_.`issuemarkettype` as issuemar9_181_0_, issuedata0_.`issuerref` as issuerref10_181_0_, issuedata0_.`liquidationstatustype` as liquida11_181_0_, issuedata0_.`offertype` as offertype12_181_0_, issuedata0_.`programtype` as program13_181_0_, issuedata0_.`votingrightstype` as votingr14_181_0_, issuedata0_.`accrualbasistype` as accrual15_181_0_, issuedata0_.`accrualconventiontype` as accrual16_181_0_, issuedata0_.`indicatorstype` as indicat17_181_0_, issuedata0_.`modificationtype` as modific18_181_0_, issuedata0_.`agreedrestriction` as agreedr19_181_0_, issuedata0_.`allowedindebtedness` as allowed20_181_0_, issuedata0_.`announcementdate` as announc21_181_0_, issuedata0_.`auctiondate` as auction22_181_0_, issuedata0_.`closingdate` as closing23_181_0_, issuedata0_.`createdate` as createdate24_181_0_, issuedata0_.`defaultclause` as default25_181_0_, issuedata0_.`facevalue` as facevalue26_181_0_, issuedata0_.`issuedate` as issuedate27_181_0_, issuedata0_.`marginable` as marginable28_181_0_, issuedata0_.`marginablemaxlimit` as margina29_181_0_, issuedata0_.`marginableminlimit` as margina30_181_0_, issuedata0_.`modificationdatetime` as modific31_181_0_, issuedata0_.`modifieddate` as modifie32_181_0_, issuedata0_.`negativepledge` as negativ33_181_0_, issuedata0_.`refrainfromacts` as refrain34_181_0_, issuedata0_.`settlementdate` as settlem35_181_0_, issuedata0_.`underlyingcount` as underly36_181_0_, issuedata0_.`useofproceeds` as useofpr37_181_0_, issuedata0_.`validity` as validity38_181_0_ from `issuedata` issuedata0_ where issuedata0_.`id`=?
Hibernate: select underlying0_.`id` as id1_329_0_, underlying0_.`calculationtype` as calculat2_329_0_, underlying0_.`indicatorstype` as indicato3_329_0_, underlying0_.`marketcenter` as marketce4_329_0_, underlying0_.`quantitydescriptiontype` as quantity5_329_0_, underlying0_.`quantitytype` as quantity6_329_0_, underlying0_.`valuationref` as valuatio7_329_0_, underlying0_.`valuationtype` as valuatio8_329_0_, underlying0_.`unittype` as unittype9_329_0_, underlying0_.instrumentDomain as instrum10_329_0_, underlying0_.`datetime` as datetime11_329_0_, underlying0_.`previousdatetime` as previou12_329_0_, underlying0_.`rank` as rank13_329_0_, underlying0_.`underlyingcount` as underly14_329_0_, underlying0_.`unitsize` as unitsize15_329_0_ from `underlyingcount` underlying0_ where underlying0_.instrumentDomain=?
Hibernate: select lastcae0_.`id` as id1_196_0_, lastcae0_.`caetype` as caetype2_196_0_, lastcae0_.`datetime` as datetime3_196_0_ from `lastcae` lastcae0_ where lastcae0_.`id`=?
Hibernate: select debtissued0_.`id` as id1_92_0_, debtissued0_.convertible as converti2_92_0_, debtissued0_.`debtindicatorstype` as debtindi3_92_0_, debtissued0_.`striptype` as striptype4_92_0_, debtissued0_.`strippbleinstrmentidentifier` as strippbl5_92_0_, debtissued0_.`coupontype` as coupontype6_92_0_, debtissued0_.`createdate` as createdate7_92_0_, debtissued0_.`modifieddate` as modified8_92_0_, debtissued0_.`overallotment` as overallo9_92_0_, debtissued0_.`strippable` as strippable10_92_0_, debtissued0_.`subscriptionduration` as subscri11_92_0_, debtissued0_.`subscriptionend` as subscri12_92_0_, debtissued0_.`subscriptionminval` as subscri13_92_0_, debtissued0_.`subscriptionstart` as subscri14_92_0_ from `debtissuedata` debtissued0_ where debtissued0_.`id`=?

repeat the last 4 selects n times


So why does it insist on fetching them via separate selects when I have just selected them as a part of the query?

Thx.

David


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 02, 2009 3:05 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Quote:
What you say is, just placing null until you access the association. How should that work? Setting the field to null and overriding the get-method?


I am not very familiar with lazy="no-proxy" but I think this is implemented like some kind of "overriding" of the get() method. This feature requires byte-code manipulation of the compiled classes as described here http://www.hibernate.org/hib_docs/v3/re ... properties

I don't know if it works with one-to-one or not, but the 'null' vs. 'not null' problem that prevents the use of an ordinary proxy shouldn't be an issue.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.