4

I'm trying to construct a complex query. My entities look as follows:

@Entity
public class Configuration{

   @Id
   @Column(name="CONF_ID")
   protected Long configurationId;

   @ManyToMany
   @MapKey(name="componentType")
   @JoinTable(name="CONF_COMP",
      joinColumns={@JoinColumn(name="CONF_ID", referencedColumnName="CONF_ID")},
      inverseJoinColumns={@JoinColumn(name="COMP_ID", referencedColumnName="componentId")})
   protected Map<String, Component> components;
}

And

@Entity
public class Component {

    @Id
    protected long componentId; 
    @ElementCollection
    protected Map<String, String> properties;

    @ManyToMany(mappedBy="components")
    private List<Configuration> configurations;

    @Column(name="COMP_TYPE")
    protected String componentType;
 }

My problem lies in querying the properties field properly. I can't seem to create a query to get all Configurations where Component A has Property Prop1 = 1 and Component B has Property Prop2=2. I tried the following without success.

Root<Configuration> conf = cq.from(Configuration.class);    
MapJoin<Configuration, String, Component> compJoin = conf.join(Configuration_.components, JoinType.LEFT);
MapJoin<Component, String, String> propJoin = compJoin.join(Component_.properties, JoinType.LEFT); 

    Predicate p1 = cb.and(
                cb.equal(mapJoin.key(), "A"),
                cb.equal(propJoin.key(), "Prop1"), cb.equal(propJoin.value(), "1"));
    Predicate p2 = cb.and(
                cb.equal(mapJoin.key(), "B"),
                cb.equal(propJoin.key(), "Prop2"), cb.equal(propJoin.value(), "2"));

Predicate[] pArray = new Predicate[]{p1, p2};
cq.where(pArray);
cq.select(conf).distinct(true);

EDIT: The query, as outputted by the logger, looks like this:

SELECT DISTINCT  t2.CONF_ID, t2.DTYPE, t2.TOTALPRICE, t2.NAME
 FROM CONFIGURATION t2 
 LEFT OUTER JOIN (CONF_COMP t3 JOIN COMPONENT t1 ON (t1.COMPONENTID = t3.COMP_ID)) ON (t3.CONF_ID = t2.CONF_ID) LEFT OUTER JOIN Component_PROPERTIES t0 ON (t0.Component_COMPONENTID = t1.COMPONENTID) 
WHERE (((((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?) AND (((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?)) AND (t2.DTYPE = ?))
bind => [7 parameters bound]

I'm guessing it is trying to find a Configuration where all the conditions apply to the SAME component maybe? It works if I apply restrictions on only one Component, but I get an empty result list when applying 2 or more, although there are entries in the DB that satisfy the conditions.

UPDATE

After following Pimgd's suggestion, i ended up with a query that looks like this:

SELECT DISTINCT t1.CONF_ID, t1.DTYPE, t1.TOTALPRICE, t1.NAME
FROM CONFIGURATION t1 LEFT OUTER JOIN (CONF_COMP t2 JOIN COMPONENT t0 ON (t0.COMPONENTID = t2.COMP_ID)) ON (t2.CONF_ID = t1.CONF_ID) 
WHERE ((( 
    t0.COMPONENTID IN (SELECT t3.COMPONENTID 
                        FROM COMPONENT t3 LEFT OUTER JOIN Component_PROPERTIES t4 ON (t4.Component_COMPONENTID = t3.COMPONENTID) 
                        WHERE ((t4.PROPERTIES_KEY = Brand) AND (t4.PROPERTIES = Intel)))) 

    AND 
    t0.COMPONENTID IN (SELECT t6.COMPONENTID 
                        FROM COMPONENT t6 LEFT OUTER JOIN Component_PROPERTIES t7 ON (t7.Component_COMPONENTID = t6.COMPONENTID) 
                        WHERE ((t7.PROPERTIES_KEY = Capacity) AND t7.PROPERTIES LIKE 4GB%))))

One criteria works, two yield no results.

Any help much appreciated!

glasspill
  • 1,290
  • 4
  • 21
  • 36
  • cb.equal(mapJoin.key() mapJoin? Where did you declare mapJoin? Are you sure you're not looking for compJoin? – Pimgd Feb 21 '12 at 12:49
  • I am not very experienced in Maps as Entity fields, and the reason is that I hardly find a valid reason for having a map in an Entity. Could you please clarify this point, maybe adding a short description of its use? – perissf Feb 21 '12 at 18:23
  • a configuration consists of several components. like a computer that is built using a motherboard, a cpu, ram etc. now, every component has a map of properties, say brand, capacity, etc. I want to get all configurations that have, say a cpu with Intel as the brand and ram with a 4GB capacity. – glasspill Feb 21 '12 at 18:27
  • My guess is that you have to split the query into different queries, one for each Component – perissf Feb 22 '12 at 21:30
  • Did you know there are badges for awarding Bounties? Next time, consider awarding a bounty on your own instead of making Community do it. – Pimgd Feb 29 '12 at 14:37

1 Answers1

1

I'm gonna say that mapJoin.key() should be compJoin.key().

Other than that, I don't see anything wrong.

If my offered solution doesn't work, here are some bonus questions for you:

  • What are the results you are getting?
  • Is it possible to show the generated queries?

An interesting bit I spotted: WHERE (((((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?) AND (((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?)) AND (t2.DTYPE = ?))

What if, I take this bit out... just for clarification. ((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?))

WHERE (((SNIP AND t0.PROPERTIES LIKE ?) AND (SNIP AND t0.PROPERTIES LIKE ?)) AND (t2.DTYPE = ?))

So yeah... Now how would you go about fixing your query? Sadly, I am no expert. But what I do know is that if you made a query with a where going WHERE t2.CONF_ID IN (Subselect for criteria A) AND t2.CONF_ID IN (Subselect for criteria B).

My suggestion would be to look up subselects and abuse those. JPA 2.0, Criteria API, Subqueries, In Expressions explains it and seems relevant enough.

Community
  • 1
  • 1
Pimgd
  • 5,983
  • 1
  • 30
  • 45
  • Updated my answer. My knowledge of the criteria API isn't sufficient to provide code - and I don't have the time to provide a complete query right now, but I hope this will help. – Pimgd Feb 21 '12 at 23:28
  • I have the same issue even when using subqueries. One criteria works, two or more yield no results. I have updated my question. Again. – glasspill Feb 23 '12 at 14:58
  • What is the output received from running the subselects? And where did t5 go? – Pimgd Feb 23 '12 at 16:00
  • there just isn't any t5. The subqueries are fine on their own, because i get correct results if i use only one of them. The question is if the structure of the query is correct as it is? If the criteria api and jpa accept multiple subqueries like that? – glasspill Feb 23 '12 at 17:53
  • 1
    ok, this helped. i figured it out. as you can see in the query i was checking the component id instead of the configuration id's. such a silly mistake. costed me all day too :| thanks for all your help and patience :) – glasspill Feb 23 '12 at 19:45