views:

457

answers:

2

I have the following setup:

Listing.cfc

component persistent="true"
{
    property name="ListingId" column="ListingId" type="numeric" ormtype="int" fieldtype="id" generator="identity"; 
        ...
    property name="Features" type="array" hint="Array of features" singularname="Feature" fieldtype="many-to-many" cfc="Feature" linktable="Listing_Feature" FKColumn="ListingId" inversejoincolumn="FeatureId";    
} 

Feature.cfc

component persistent="true" table="Feature" schema="dbo" output="false"
{
    property name="FeatureId" column="FeatureId" type="numeric" ormtype="int" fieldtype="id" generator="identity"; 
    property name="FeatureDescription" column="FeatureDescription" type="string" ormtype="string";
    ...    
    /*property name="Listings" fieldtype="many-to-many" cfc="Listing" linktable="Listing_Feature" fkcolumn="FeatureId" inversejoincolumn="ListingId" lazy="true" cascade="all" orderby="GroupOrder";*/

} 

I can select all listings that have a particular feature using:

<cfset matchingListings = ormExecuteQuery("from Listing l left join l.Features as feature where feature.FeatureId = :feature",{feature = 110}) />

Which is fine, however, I'd like to be able to select all listings that have multiple features (for example a listing that has both "Dishwasher" AND "Garage")

After a couple hours of googling and looking through hibernate documentation haven't been able to find a solution that won't give me an error. My guess is that the solution is pretty simple and I am just over-thinking it...anyone have any suggestions?

A: 

This should work:

<cfset featurelist = "110,113,125"/>
<cfset matchingListings = ormExecuteQuery("from Listing l left join l.Features as feature where feature.FeatureId IN (#featurelist#)")/>

If you want to go with bound parameters you will have to do some extra work, because hibernate doesn't like ColdFusion Lists and/or Arrays as bound parameters this time. You can find some extra info here

Andreas Schuldhaus
This won't actually give me the result I want though. This will give me any Listing with a featureid of 110, 113, OR 125, not only listings with all 3 features.
Jeremy Battle
Ok. I see. Sorry for the confusion. Then you would have to group the result and use HAVING clause to check whether your Listing Object matches *ALL* the criteria. Saw that You, Henry et. al. already posted a solution to cf-orm-dev Google group.
Andreas Schuldhaus
+3  A: 

I don't believe this to be the most efficient way to do this, however, it does produce the result I want

<cfset matchingListings = ormExecuteQuery("Select l.ListingId from Listing l
  left join l.Features as featureone left join l.Features as featuretwo
  left join l.Features as featurethree 
  where featureone.FeatureId = 108
    and featuretwo.FeatureId = 110
    and featurethree.FeatureId = 113") />

This will give me only listings that have all the features I am looking for but, it does a LOT of joining and looking at the hibernate SQL log is producing:

select listing0_.ListingId as col_0_0_ 
from dbo.Listing listing0_ 
left outer join Listing_Feature features1_ on listing0_.ListingId=features1_.ListingId 
left outer join dbo.Feature feature2_ on features1_.FeatureId=feature2_.FeatureId
left outer join Listing_Feature features3_ on listing0_.ListingId=features3_.ListingId 
left outer join dbo.Feature feature4_ on features3_.FeatureId=feature4_.FeatureId 
left outer join Listing_Feature features5_ on listing0_.ListingId=features5_.ListingId 
left outer join dbo.Feature feature6_ on features5_.FeatureId=feature6_.FeatureId 
where 1=1 
and feature2_.FeatureId=108 
and feature4_.FeatureId=110 
and feature6_.FeatureId=113

It just seems like there must be a more efficient way to do this in HQL


Jon Messer on the cf-orm-dev mailing list gave me what I believe is the most correct solution to this question posting it here for everyone:

"As far as I know ORMExecuteQuery doesn't handle list parameters, so if you wanted to param them and return objects you'd have to do something like

<cfset featureIds = [javaCast('int',108), javaCast('int',110), javaCast('int',113)] >

<cfset q = ormGetSession().createQuery("
    select l.ListingId 
    from Listing l 
        join l.features as f 
    where f.FeatureId in (:features)
    group by l.ListingId
    having count(*) = #arrayLen(featureIds)#
") /> 

<cfset q.setParameterList('features', featureIds) /> 

<cfset matchingListings = q.list() />

"

Thanks Jon!

Jeremy Battle
at least it is correct, I would do it this way if I have to stay in HQL (not writing custom, more efficient SQL)
Henry
I've added the most correct solution to the bottom of this answer.
Jeremy Battle