tags:

views:

108

answers:

1

Hello,
Learning how to write JPA query. Please advise me whether it possible to write the below queries more efficiently, may be in a single select statement. May be a join, but not sure how to do it.

class Relationship {

  @ManyToOne
  public String relationshipType;  //can be MANAGER, CUSTOMER etc

  @ManyToOne
  public Party partyFrom; // a person who has a relation

  @ManyToOne
  public Party partyTo; // a group a person relate to
}

Queries:

        String sql = "";
        sql = "select rel.partyTo";
        sql += " from Relationship rel";
        sql += " where rel.partyFrom = :partyFrom";
        sql += " and rel.relationshipType= :typeName";
        Query query = Organization.em().createQuery(sql);
        query.setParameter("partyFrom", mgr1);
        query.setParameter("typeName", "MANAGER");
        List<Party> orgList = query.getResultList();

        String sql2 = "";
        sql2 = "select rel.partyFrom";
        sql2 += " from Relationship rel";
        sql2 += " where rel.partyTo = :partyToList";
        sql2 += " and rel.relationshipType = :typeName2";
        Query query2 = Organization.em().createQuery(sql2);
        query2.setParameter("partyToList", orgList);
        query2.setParameter("typeName2", "CUSTOMER");
        List<Party> personList2 = query2.getResultList();

Both the queries work. Query 1 returns a list of groups, where the person (mgr1) has a relation MANAGER with. Query 2 returns all the Persons they are CUSTOMER to the groups returned by query 1. In effect, I get a list of Person they are belong to (customer) the same group where the Person (mgr1) has a relation MANAGER with.

Is it possible to combine them into single sql statement so possibly only one db access?

A: 

You literally nest one query inside the other, and use a "where in" clause to specify that the outer query should fetch customers from the inner query.

select rel2.partyFrom
from Relationship rel2
where rel2.relationshipType = :typeName2 /* customer */
and rel2.partyTo.id in 
      (select rel.partyTo.id
      from Relationship rel
      where rel.partyFrom = :partyFrom
      and rel.relationshipType = :typeName)

Your calling code passes typeName, typeName2, and partyFrom parameters as before. PartyTo parameter is not needed, since the data comes from the subselect (inner query.)

You can achieve the same thing using a self join, with a where clause that filters managers on the left side, and customers on the right side, but using an 'in' clause is semantically clearer.

EDIT: I addded .id to the subselect, which I think is needed.

mdma
thanks!!.. worked perfect (without the id -- EDIT).. will accept the answer, wait to see whether someone comment on the performance of inner query.. whether this is the optimal way to it .. thanks again..
bsreekanth
The subquery will be quick, since it's not a correlated query - it's computed just once, just as with your original code. Just today, I replaced a LEFT JOIN with a WHERE IN and changed the performance of the query from 18s to 5s on SQL server. You don't say which db you are using, but in general a subquery can outperform a join when you are only doing an existence test and not using any of the data.
mdma
thanks.. plan to use PostgreSQL or Oracle.. I use hibernate ORM, so possibly it may compatible with others as well.. can you please elaborate on an existence test vs not using any of the data .. learning new stuff everyday :-) thanks..
bsreekanth
With a join, you typically do that to bring in other fields from the joined table. Some people also use a outer join as a kind of existence test - does this key exist in the set of joined values. But since none of the other fields are needed from the joined table, it's much more efficient to use a WHERE IN clause.
mdma
thanks for the explanation.
bsreekanth