How to fetch Data using JPA for dynamic Where Clause - java

I am new to JPA. I am currently using JPA2.0 in WAS 8.5.5
I have a search screen where we have lot of search criteria's. Now, I have to create Query in JPA such a way that any criteria user has selected, it automatically check for that particular column in DB.
I am not able to find any solution on that. It seems to me that for every search criteria, I have to write new named Query.
Any suggestion or pointers will be appreciated.

You could do it in named query, but you would have to check every single possible criteria if it is null in order to avoid null values affect the results. Beware of unnecessary inner joins, each nullable relation should be included as left join
select e from Employee e left join e.department d
where (:name is null or e.name = :name)
and (:email is null or e.email = :email)
and (:deptId is null or d.id = :deptId)
...
However, depending on complexity of possible combinations, better option could be to not use named queries, but dynamically construct a JPQL depending on selected criteria.

Related

'ORDER BY' in JPQL-query adds 'CROSS JOIN' to produced sql thus reducing final results

I’m to create query with ordering by field of related entity, like this:
SELECT p FROM Patient p ORDER BY p.doctor.name
And sql-query that Hibernate builds based on that of JPQL uses CROSS JOIN with condition (effectively, inner join):
select patient0_.id as id1_1_, patient0_.doctor_id as doctor_i3_1_, patient0_.name as name2_1_
from patient patient0_ cross join doctor doctor1_
where patient0_.doctor_id=doctor1_.id
order by doctor1_.name
As a side-effect, all patients with nulls in ‘doctor’ field are excluded from result set. Is there any option I can switch to hint Hibernate to use LEFT JOIN in such cases? Can’t such a behavior be considered a bug? From common sense’ point, just adding ordering shoud not affect result count.
Example is intentionally simplified. In real world it’s a dynamically built criteria-query with variable set of filters and sortings. So I can not work it around and use explicit LEFT JOIN in JPQL.
I reproduced behavior of simplified example in version 5.3.9.Final and in latest 5.4.15.Final.
Appreciate any help.
You should use explicit join instead of hibernate implicit join.
SELECT p FROM Patient p left join fetch p.doctor d ORDER BY d.name
If there is lazy relation and it is not wanted that doctor is loaded, fetch should not be used.
SELECT p FROM Patient p left join p.doctor d ORDER BY d.name
This is how the implicit join is defined for JPA, as an inner join. So if you want to use left join semantics, you will have to use a an explicit left join.
If you want something more dynamic, I can recommend you take a look at a query builder like Blaze-Persistence that has left join semantics for implicit joins.
Here is a Spring WebMvc example application that supports dynamic filtering and sorting for paginated datatables: https://github.com/Blazebit/blaze-persistence/tree/master/examples/spring-data-webmvc

How to select in Hibernate criteria if there is no relationship between the tables? [duplicate]

I'd like to use Hibernate's criteria api to formulate a particular query that joins two entities. Let's say I have two entities, Pet and Owner with a owner having many pets, but crucially that association is not mapped in the Java annotations or xml.
With hql, I could select owners that have a pet called 'fido' by specifying the join in the query (rather than adding a set of pets to the owner class).
Can the same be done using hibernate criteria? If so how?
Thanks,
J
This is indeed possible with criteria:
DetachedCriteria ownerCriteria = DetachedCriteria.forClass(Owner.class);
ownerCriteria.setProjection(Property.forName("id"));
ownerCriteria.add(Restrictions.eq("ownername", "bob"));
Criteria criteria = getSession().createCriteria(Pet.class);
criteria.add(Property.forName("ownerId").in(ownerCriteria));
Update: This actually performs a sub-query instead of a join but it allows you to use Criteria on two entities that do not have a hibernate relationship defined.
My understanding is that if you do this using HQL, you are creating a Cartesian join with a filter, rather than an inner join. Criteria queries do not support doing this.
In NHibernate you can use subqueries which are defined as DetachedCriteria. Not sure if it works the same in Java, most probably it is the same:
DetachedCriteria pets = DetachedCriteria.For<Pet>("pet")
.SetProjection(Projections.Property("pet.ownername"))
.Add(/* some filters */ );
session.CreateCriteria(typeof(Owner))
.Add(Subqueries.PropertyIn("name", pets);
Assumed that it is joined using the name of the owner.
Criterion ownerCriterion = Restrictions.sqlRestriction(SELECT ownerId FROM Owner WHERE ownerName ='bob');
Criteria criteria = getSession().createCriteria(Pet.class);
criteria.createCriteria("ownerId").add(ownerCriterion);
There's a SQLCriterion, which you can give arbitrary SQL, and add to your Criteria. In the SQL string, the token {alias} "will be replaced by the alias of the root entity."

hibernate: how to map a Set association so that HQL order by is preserved

OK, this looks mostly like (another) bug/unimplemented functionality in Hibernate.
Is it possible to map a Set association in such a way that the "order by" clause in HQL is respected?
For example, say you have 2 entities: PageEntity and QuestionEntity. A page has a Set.
How do I make the following HQL work:
from
PageEntity p
left outer join fetch p.questionEntities q
order by
q.orderIndex
What I don't want:
to use sort in Java (we have "order by" in SQL so we don't have to do this!). SortedSet, Comparators etc. are out of the question
to map it as a List with a <list-index>. This will add the "order by" to all SQL queries, and I don't want this
to use a <set order-by="orderIndex"> because again, this will be applied to all queries
Debugging, I see that the implementation of Set that is being used is org.hibernate.collection.PersistentSet which wraps a Set. The implementation of the wrapped Set is HashSet. I would expect Hibernate to be smart enough to use a LinkedHashSet instead, so that it can honor my "order by" HQL clause.
I'm pretty sure Hibernate can't do this for you, and would recommend using a List instead. To remove duplicates introduced by the join, you can use the distinct keyword. The resulting List will be just as good as any Set.
If you still need them in an ordered Set (maybe there's a 3rd party API involved) you could create your own LinkedHashSet and move all objects there:
List<PageEntity> list = runQuery(...);
return new LinkedHashSet<PageEntity>(list);
It seems you want to get results sorted by q.orderIndex but
do you want the resulting list of PageEntity to be sorted?
or (as I think) do you want the set of QuestionEntitys for each PageEntity to be sorted?
If you want the latter: Sets in Java do not preserve order.
After your edit: no Hibernate does not have to be smart: if you define your collection to be a Set it will be treated as a set. There are many applications where preserving the order costs resources and is not needed.
order by applies to the results of the query. Not to the entities in the collection of questions. I don't think it's a bug: what would it do if one of the returned page entity is already loaded in the session, using another query with another order by clause?
If you want an ordered list of questions, select the questions:
select q from QuestionEntity q inner join fetch q.page p order by q.orderIndex
or
select p, q from PageEntity p
left outer join fetch p.questionEntities q
order by q.orderIndex
should do the trick.

Hibernate: How to query to an object which it's collection proprty contains a specific value?

I've got a Player Object in which there is a Collection<Stock>. I'm willing to write a hibernate query which returns the list of players who have a specific stock (for example stock.symbol="**").
any ideas?
No problem use HQL with join syntax.
see it here
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html#queryhql-joins-forms
For example:
Player p join p.myStocks stock where stock.symbol = :symbol

Hibernate Criteria and multiple join

is possible with Hibernate criteria do it?
select A.something, B.something, C.something, D.something
from A JOIN B on A.id = B.id_fk
JOIN C ON B.id = C.id_fk
JOIN D ON C.id = D.id_fk;
I have got exactly the same problem, and was able to resolve it like this:
return criteria.createCriteria(A.class)
.createCriteria("b", "join_between_a_b")
.createCriteria("c", "join_between_b_c")
.createCriteria("d", "join_between_c_d")
.add(Restrictions.eq("some_field_of_D", someValue));
Note: "b", "c" and "d" in code above refer to attribute names in A, B and C classes, correspondingly (class A has attribute b and so on).
For this solution you don't even need to have lazy and fetch parameters to be set in your A.hbm.xml.
There are some good examples in the Hibernate Reference material that show to use setFetchMode to fetch associations with an outer join.
An example is:
List books = sess.createCriteria(Book.class)
.setFetchMode("chapters", FetchMode.EAGER)
.setFetchMode("reviews", FetchMode.EAGER)
.list();
There is also information there about different fetching stragies that may be of use to you.
Try setting the fetch mode in your criteria, like:
criteria.setFetchMode(..., FetchMode.EAGER)
This creates a join query.
You may find more details here.
Yes, in fact there are several ways of doing this:
When mapping the association, set its lazyness to false and its fetch mode to join. This will affect all criteria queries.
Use setFetchMode as detailed by the other answers.
Use criteria.createAlias (or createCriteria). This also allows you to further restrict the rows you want joined.

Categories

Resources