Howto setup this SQL query with CriteriaBuilder? - java

to select an overview from a database I have to use a statement like this:
select a1.begin, a1.end, b1.name, c1.name, d1.name, e1.name, f1.name, ...
from a2, b1, b2, c1, c2, a1
left outer join (d1 join d2 on d2.id = d1.parent and d1.user = ?) on
d1.deleted = 0 and d1.parent = a1.d1_id
left outer join (e1 join e2 on e2.id = e1.parent and e1.user = ?) on
e1.deleted = 0 and e1.parent = a1.e1_id
left outer join (f1 join f2 on f2.id = f1.parent and f1.user = ?) on
f1.deleted = 0 and f1.parent = a1.f1_id
where .... a lot more stuff...
The statement works like a charm and the database has no problem to process it really fast. Believe me, there's definitely no other way to get the required data.
Until now I was pretty proud to say, that I don't have a single word of pure EJB-QL or even SQL in our java project. But now I'm not sure if there is any way to build such a statement by using the CriteriaBuilder. I have absolutely no idea how to create an outer join with more than the single criteria or even with a nested inner join.
Any hints greatly appreciated.
Thanks
Hennes

It's hard to give specific help without knowing the object model, but I believe you want to use the join method and specify the join type. Using it you can nest joins that will use the relationship defined on the specified attribute mapping. Explain action of the criteria API can be found here:
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Criteria#Join
Eclipselink also supports the 'on' clause in JPQL and criteria queries through feature https://bugs.eclipse.org/bugs/show_bug.cgi?id=367452

first join:
criteria=session.createCriteria(mappingClass.class).createAlias("propertyName","firstAliasName");
second join:
criteria.createAlias("firstAliasName.propertyName","secondAliasName");
and so on.
That will do OFC if you are joining by mapped relations. If not than read this:Criteria API
. The simplest way to do such think it would by by HPQL or JPQL, however you will probably wont get mapped enities as result, only ResultSet with columns (but you won't get it anyway I think)

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

Can Hibernate map the result set of a 1:M join in a subselect to a parent with a child collection?

I am attempting to map the result of a SQL left join to an object structure of the form parent -> [child], where a condition on the SQL query restricts the result to one parent. The result set contains n rows because there are n children, but of course each row only belongs to the single parent.
I'm using Hibernate, and have placed a #Subselect on my 'parent' entity, and this contains my entire left join query.
#Subselect("SELECT x.*, y.* FROM x INNER JOIN y on x.a = y.a WHERE x.a = 1")
public class X {
... X attributes
private List<Y>;
}
public class Y {
... Y attributes
}
How can I instruct Hibernate to 'collapse' the columns on the left side of the result set to a single parent object, and coalesce the remaining columns from each row into many instances of 'child' which are added to the list on the parent?
Is this possible, or do I need to join using Hibernates' #OneToMany annotation. This does work for me, but results in two separate calls to the database, which I feel is inefficient.
This can be achieved using JPQL (or HQL if you are using Hibernate) and the 'join fetch' command :
SELECT a from Author a left join fetch a.books;
Attempting to perform multiple joins in this manner can produce a 'MultipleBagFetchException'. This can be worked around, but in any case, by farming the joins (especially several joins) off to the DB server, we produce a cartesian product result set which can grow very large.
Counterintuitively, it can actually be more efficient to make multiple round trips to the database (using batching to mitigate the N + 1) problem, and then join the parent and children in memory using ORM.
Thanks to https://stackoverflow.com/users/1025118/vlad-mihalcea for many pointers on this topic across the web, in particular this article on his blog - https://stackoverflow.com/users/1025118/vlad-mihalcea

Why this hql statement is like an inner join?

Why this HQL statement is like an inner join?
select
user.id,
user.allocationVersion,
user.tx.statusId,
user.userId,
user.nameFirst,
user.nameLast,
user.email1,
user.statusId,
user.tx.name,
user.note1,
user.note2
from
module.bb.jpa.User as user
where user.clientId = :clientId
order by user.id DESC
The TX class which is referenced in the User class can be null. Why do I get a result as if I did an inner join? I just get Users with a TX but I want all of them.
The HQL reference talks about this in section 14.4:
HQL supports two forms of association joining: implicit and explicit.
The queries shown in the previous section all use the explicit form,
that is, where the join keyword is explicitly used in the from clause.
This is the recommended form.
The implicit form does not use the join keyword. Instead, the
associations are "dereferenced" using dot-notation. implicit joins can
appear in any of the HQL clauses. implicit join result in inner joins
in the resulting SQL statement.
from Cat as cat where cat.mate.name like '%s%'
[highlighting by me].
What your HQL is basically equivalent to is:
select
user.id,
user.allocationVersion,
user.tx.statusId,
user.userId,
user.nameFirst,
user.nameLast,
user.email1,
user.statusId,
user.tx.name,
user.note1,
user.note2
from
module.bb.jpa.User as user
join module.bb.jpa.Tx as tx -- This is an abbreviated inner join
where user.clientId = :clientId
order by user.id DESC
To "correct" this behaviour you will have to explicitly specify a left outer join or left join for short.
Because you have referenced tx.name in user. It should not work if you access it in Java but in HQL to get the value for this column Hibernate make implicit join. To get all values you should do it explicitly and include outer join option.

How to fetch Data using JPA for dynamic Where Clause

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.

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