hi all - I am having problems figuring out if this is an issue or correct behaviour, and if it is correct behaviour, how to achieve desired result. I have 2 entities, ProductionOrder and Model. Each order has one model, model can have multiple orders against it.
Code:
<hibernate-mapping package="model.application">
<class name="ProductionOrder" table="PO" lazy="false" batch-size="10">
<composite-id name="key" class="ProductionOrderKey">
<key-property name="orderNumber" column="POORDNO"/>
<key-property name="headerDetail" column="POHD" />
<key-property name="modelCode" column="POMDL" />
<key-property name="type" column="POTYPE" />
<key-property name="option" column="POOPT" />
<key-property name="hesColour" column="POHESC" />
<key-property name="trimColour" column="POTRMCOL" />
</composite-id>
<property name="orderedQuantity" type="big_decimal" column="POQTY" not-null="true"/>
<property name="shippedQuantity" type="big_decimal" column="POSHQTY" not-null="true"/>
<property name="deliveryYear" type="int" column="PODLVYR" not-null="true"/>
<property name="deliveryMonth" type="int" column="PODLVMTH" not-null="true"/>
<property name="deliveryDate" formula="PODLVYR || PODLVMTH"/>
<many-to-one name="model" class="Model"
fetch="join" lazy="false" not-found="ignore" insert="false" update="false">
<column name="POMDL"/>
<column name="POTYPE"/>
<column name="POOPT"/>
<column name="POHESC"/>
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping package="model.application">
<class name="Model" table="MM" lazy="false">
<composite-id name="hmModel" class="InternationalVehicleModel">
<key-property name="modelCode" column="MMMDL"/>
<key-property name="type" column="MMTYPE" />
<key-property name="option" column="MMOPT" />
<key-property name="hesColour" column="MMHESC" />
</composite-id>
<component name="auhModel" lazy="false">
<property name="modelCode" type="string" column="MMNMDL" not-null="true"/>
<property name="doors" type="string" column="MMNDOOR" not-null="true"/>
<property name="transmission" type="string" column="MMNTRAN" not-null="true"/>
<property name="grade" type="string" column="MMNGRADE" not-null="true"/>
<property name="colour" type="string" column="MMNCOL" not-null="true"/>
<property name="year" type="string" column="MMNYEAR" not-null="true"/>
</component>
</class>
</hibernate-mapping>
When I need to find an order, I use Criteria API and I would usually add some restrictions on model, eg. (you can ignore populateCriteria/populateStringCriteria - all they do is add Property.eq or Property.like to the criteria).
Code:
DetachedCriteria criteria = DetachedCriteria
.forClass(ProductionOrder.class);
criteria.createAlias("model", "model");
criteria.setFetchMode("model", FetchMode.JOIN);
populateCriteria(criteria, CriteriaOperation.LIKE, "key.orderNumber", "7%");
populateCriteria(criteria, CriteriaOperation.EQUALS, "key.headerDetail", "1");
populateCriteria(criteria, CriteriaOperation.BETWEEN, "deliveryDate",
new String[] {startDate, endDate});
populateStringCriteria(criteria, "model.auhModel.modelCode", model
.getModelCode());
populateStringCriteria(criteria, "model.auhModel.transmission", model
.getTransmission());
populateStringCriteria(criteria, "model.auhModel.doors", model.getDoors());
populateStringCriteria(criteria, "model.auhModel.grade", model.getGrade());
populateStringCriteria(criteria, "model.auhModel.colour", model.getColour());
populateStringCriteria(criteria, "model.auhModel.year", model.getYear());
populateWarehouseCriteria(criteria, "warehouse", warehouse);
When I run this query - I see the generated SQL that has the join between order and model table - just what I need.
Code:
DEBUG [org.hibernate.SQL] - select this_.poordno as poordno5_1_, this_.pohd as pohd5_1_, this_.pomdl as pomdl5_1_, this_.potype as potype5_1_, this_.poopt as poopt5_1_, this_.pohesc as pohesc5_1_, this_.potrmcol as potrmcol5_1_, this_.powh as powh5_1_, this_.poqty as poqty5_1_, this_.poshqty as poshqty5_1_, this_.podlvyr as podlvyr5_1_, this_.podlvmth as podlvmth5_1_, this_.PODLVYR || this_.PODLVMTH as formula0_1_, model1_.mmmdl as mmmdl1_0_, model1_.mmtype as mmtype1_0_, model1_.mmopt as mmopt1_0_, model1_.mmhesc as mmhesc1_0_, model1_.mmnmdl as mmnmdl1_0_, model1_.mmndoor as mmndoor1_0_, model1_.mmntran as mmntran1_0_, model1_.mmngrade as mmngrade1_0_, model1_.mmncol as mmncol1_0_, model1_.mmnyear as mmnyear1_0_ from mpop this_ inner join mmmp model1_ on this_.pomdl=model1_.mmmdl and this_.potype=model1_.mmtype and this_.poopt=model1_.mmopt and this_.pohesc=model1_.mmhesc where this_.poordno like ? and this_.pohd=? and this_.PODLVYR || this_.PODLVMTH between ? and ? and model1_.mmnmdl=? and this_.powh=? order by this_.PODLVYR || this_.PODLVMTH asc
As you can see, the query has all the columns required to populate
both ProductionOrder and Model - just what I need! But then comes the problem - when Hibernate hydrates entities (ProductionOrder and Model) - it runs another query to read only model table - and it runs this query for each row returned from the first query:
Code:
2010-09-28 13:30:36,791 DEBUG [org.hibernate.loader.Loader] - loading entity: [model.application.Model#component[modelCode,type,option,hesColour]{option= , type=KQ , hesColour=HC001 , modelCode=TSTMDL1 }]
2010-09-28 13:30:36,791 DEBUG [org.hibernate.jdbc.AbstractBatcher] - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
2010-09-28 13:30:36,791 DEBUG [org.hibernate.SQL] - select model0_.mmmdl as mmmdl1_0_, model0_.mmtype as mmtype1_0_, model0_.mmopt as mmopt1_0_, model0_.mmhesc as mmhesc1_0_, model0_.mmnmdl as mmnmdl1_0_, model0_.mmndoor as mmndoor1_0_, model0_.mmntran as mmntran1_0_, model0_.mmngrade as mmngrade1_0_, model0_.mmncol as mmncol1_0_, model0_.mmnyear as mmnyear1_0_ from mmmp model0_ where model0_.mmmdl=? and model0_.mmtype=? and model0_.mmopt=? and model0_.mmhesc=?
Hibernate: select model0_.mmmdl as mmmdl1_0_, model0_.mmtype as mmtype1_0_, model0_.mmopt as mmopt1_0_, model0_.mmhesc as mmhesc1_0_, model0_.mmnmdl as mmnmdl1_0_, model0_.mmndoor as mmndoor1_0_, model0_.mmntran as mmntran1_0_, model0_.mmngrade as mmngrade1_0_, model0_.mmncol as mmncol1_0_, model0_.mmnyear as mmnyear1_0_ from mmmp model0_ where model0_.mmmdl=? and model0_.mmtype=? and model0_.mmopt=? and model0_.mmhesc=?
2010-09-28 13:30:37,260 DEBUG [org.hibernate.jdbc.AbstractBatcher] - about to open ResultSet (open ResultSets: 0, globally: 0)
2010-09-28 13:30:37,260 DEBUG [org.hibernate.loader.Loader] - result row: EntityKey[model.application.Model#component[modelCode,type,option,hesColour]{option= , type=KQ , hesColour=HC001 , modelCode=TSTMDL1 }]
2010-09-28 13:30:37,260 DEBUG [org.hibernate.jdbc.AbstractBatcher] - about to close ResultSet (open ResultSets: 1, globally: 1)
2010-09-28 13:30:37,260 DEBUG [org.hibernate.jdbc.AbstractBatcher] - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2010-09-28 13:30:37,260 DEBUG [org.hibernate.engine.TwoPhaseLoad] - resolving associations for [model.application.Model#component[modelCode,type,option,hesColour]{option= , type=KQ , hesColour=HC001 , modelCode=TSTMDL1 }]
2010-09-28 13:30:37,260 DEBUG [org.hibernate.engine.TwoPhaseLoad] - done materializing entity [model.application.Model#component[modelCode,type,option,hesColour]{option= , type=KQ , hesColour=HC001 , modelCode=TSTMDL1 }]
2010-09-28 13:30:37,260 DEBUG [org.hibernate.loader.Loader] - done entity load
2010-09-28 13:30:37,260 DEBUG [org.hibernate.engine.TwoPhaseLoad] - done materializing entity [model.application.ProductionOrder#component[orderNumber,headerDetail,modelCode,type,option,hesColour,trimColour]{option= , headerDetail=1, type=KQ , trimColour=BK, orderNumber=760271400, hesColour=HC001 , modelCode=TSTMDL1 }]
Can hibernate gurus please advise if desired behaviour is possible at all? I mean - is it possible to use the values from the first query to populate both entities, not just entity keys and then go to database again to resolve associations? Any input appreciated - I tried different ways, HQL with fetch join, different ResultTransformers - it all goes to the TwoPhaseLoad mechanism - looks like it ignores anything but keys from the first result set and then it resolves entities by fetching corresponding row using the id it got from the initial resultset.