JPA Hibernate Generated Left Join ignored using criteria builder - java

I have an entity (Person) having a #ManyToOne relation with another entity (Availability) and other entities. When I get the Persons, I don't have the persons where the Availability is Null as Hibernate do an inner join (If I have an eager fetch), or a Select if Lazy fetch.
In the same time, I try to create another bean from the result so I use:
query.select(builder.construct(MyPerson.class,root.get("availability").get("date").....)
This will generate
select a.date, ... from Person p, Availability a Where p.availId = a.id.
As I need a Left join, I have added to my code :
Join<Availability, person> avail = root.join("availability", JoinType.LEFT);
Strange, it will generate an LEFT OUTER join but still use the old request
select **a2**.date, ...
from Person p,
LEFT OUTER JOIN Availability a1 on a1.id = p.availId
,**Availability a2**
**Where p.availId = a2.id.**
What is wrong with it?
The Only case when it generate only the Left Join is when I construct the new bean with the root. (with a Lazy Loading), but it will generate too many other queries.
query.select(builder.construct(MyPerson.class,root)

Finally, I find the Solution. In fact, I don't need to use
Join avail = root.join("availability", JoinType.LEFT);
So I have removed it, and while creating my new bean I do :
query.select(builder.construct(MyPerson.class,
root.join("availability", JoinType.LEFT).get("date"))
Now I have only one generated query with the LEFT OUTER JOIN.

Related

JPA & Hibernate: Eager loading performing subsequent queries to fetch all data, instead of doing it in just one query

I have the following doubt. I would like to know why when using JPA and Hibernate, when performing an Eager loading in a ManyToOne or OneToMany relationship, it calls the DB in order to obtain the Entity information but additionally, produces subsequent queries to fetch every child.
On the other side, when using a query with JOIN FETCH it performs the query as I would expect it to be, taking the information all at once, since the fetchType is denoted as "EAGER".
Here it is a simple example:
I have the Class Student which has ManyToOne relationship with the Class Classroom.
#Entity
#Table(name = "STUDENT")
public class Student {
#ManyToOne(optional = true, fetch = FetchType.EAGER)
#JoinColumn(name = "ClassroomID")
private Classroom mainClass;
In the other side there is the class named Classroom as follows:
#Entity
public class Classroom {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "mainClass", fetch = FetchType.EAGER)
private List<Student> studentsList;
When obtaining the Classroom object, it performs one query to obtain the information from itself and subsequent queries to obtain the information of each student contained in the studentsList for every classRoom object.
First query:
Hibernate:
/* SELECT
r
FROM
Classroom r
LEFT JOIN
r.classStudents */
select
classroom0_.id as id1_0_,
classroom0_.number as number2_0_
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
And then it performs the next query as many times as Students assigned to each classroom.
Hibernate:
/* load one-to-many com.hw.access.Classroom.classStudents */
select
classstude0_.ClassroomID as Classroo4_0_1_,
classstude0_.id as id1_1_1_,
classstude0_.id as id1_1_0_,
classstude0_.FIRST_NAME as FIRST_NA2_1_0_,
classstude0_.LAST_NAME as LAST_NAM3_1_0_,
classstude0_.ClassroomID as Classroo4_1_0_
from
STUDENT classstude0_
where
classstude0_.ClassroomID=?
The question is: Why doesn't it takes the Information all at once? Why doesn't it takes the information in just one query? As it is already performing the Join clause there.
Why just when adding Fetch explicitly in the query, it does what it is requested?
For example:
SELECT
r
FROM
Classroom r
LEFT JOIN FETCH
r.classStudents */
And then, the output query does takes all the information in just one query:
Hibernate:
select
classroom0_.id as id1_0_0_,
classstude1_.id as id1_1_1_,
classroom0_.number as number2_0_0_,
classstude1_.FIRST_NAME as FIRST_NA2_1_1_,
classstude1_.LAST_NAME as LAST_NAM3_1_1_,
classstude1_.ClassroomID as Classroo4_1_1_,
classstude1_.ClassroomID as Classroo4_0_0__,
classstude1_.id as id1_1_0__
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
As you have a OneToMany relationship from Classroom to Student, using a single query would cause the Classroom fields to be repeated for each line.
Now imagine you have a second OneToMany relationship from Classroom to, say Course; if, for a given Classroom you have N Students and M Courses, you would have a query returning N+M rows, each containing the same fields of class Classroom.
I found it described in https://vladmihalcea.com/eager-fetching-is-a-code-smell/
under EAGER fetching inconsistencies:
Both JPQL and Criteria queries default to select fetching, therefore issuing a secondary select for each individual EAGER association. The larger the associations’ number, the more additional individual SELECTS, the more it will affect our application performance.
Also, note that Hibernate similarly ignoreg fetching annotations for HQL queries:
https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_selects
Hibernate ignores my outer-join="true" or fetch="join" setting and fetches an association lazily, using n+1 selects!
HQL queries always ignore the setting for outer-join or fetch="join" defined in mapping metadata. This setting applies only to associations fetched using get() or load(), Criteria queries, and graph navigation. If you need to enable eager fetching for a HQL query, use an explicit LEFT JOIN FETCH.
By default fetchtype is lazy, that mean if you dont ask for the List in your request Hibernate will not collect it.
In first request you ask for all attribute of Classroom r including the Student list so Hibernate will load them lazily (after finding out that you need them).
But when fetchtype is set to eager hibernate collect it even if you dont ask it.

JPA Criteria - fetch with where clause

I have following query which is working fine:
public ContractorContractor findContractorByName(String contractorName) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ContractorContractor> query = builder.createQuery(ContractorContractor.class);
Root<ContractorContractor> root = query.from(ContractorContractor.class);
query.select(root).distinct(true);
Predicate namePredicate = builder.like(root.get(ContractorContractor_.name), contractorName);
query.where(builder.and(namePredicate));
return em.createQuery(query).getSingleResult();
}
Above query gives me single contractor by name or throws exception.
Now I would like to do same thing but get more informations about contractor (add the fetch to another child of contractor) but with following query I do not get result (org.springframework.dao.EmptyResultDataAccessException: No result found for query is thrown). Query with fetch:
public ContractorContractor findContractorByName(String contractorName) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ContractorContractor> query = builder.createQuery(ContractorContractor.class);
Root<ContractorContractor> root = query.from(ContractorContractor.class);
root.fetch(ContractorContractor_.countries);
query.select(root).distinct(true);
Predicate namePredicate = builder.like(root.get(ContractorContractor_.name), contractorName);
query.where(builder.and(namePredicate));
return em.createQuery(query).getSingleResult();
}
Can anybody tell me what I am doing wrong and why in above query?
It appears that you have accidentally limited the domain of your query by adding a inner fetch join with the addition of the following statement:
root.fetch(ContractorContractor_.countries);
As per JPA 2.1, JSR 338, section 6.5.4
[...] A fetch join has the same join semantics as the corresponding inner or outer join [...]
Thus changing the implicit inner fetch join (JoinType.INNER) to an (outer) left fetch join should solve your problem:
root.fetch(ContractorContractor_.countries, JoinType.LEFT);
This side effect might seem a little unexpected which is probably why the authors of the specification added the following comment (although in context with standard joins, section 4.4.7):
Application developers should use caution in defining identification variables because the domain of the query can depend on whether there are any values of the declared type.

left join with spring data jpa and querydsl

I am using spring data jpa and querydsl and trapped on how to write simple nice query to left join two tables.
Suppose I have an Project entity and a Task entity with OneToMany relationship defined in Project, I would like to do something like:
select * from project p left join task t on p.id = t.project_id where p.id = searchTerm
select * from project p left join task t on p.id = t.project_id where t.taskname = searchTerm
In JPQL, it should be:
select distinct p from Project p left join p.tasks t where t.projectID = searthTerm
select distinct p from Project p left join p.tasks t where t.taskName = searthTerm
I have a ProjectRepository interface, which extends JpaRepository and QueryDslPredicateExecutor.
That gives me access to method:
Page<T> findAll(com.mysema.query.types.Predicate predicate, Pageable pageable)
I know that left join can be easily achieved by creating a new JPAQuery(entityManager). But I do not have entity manager explicitly injected with spring data jpa.
Is there nice and simple way to build a predicate with left join?
Wish someone here have experienced this and is able to give me an example.
Thank you.
Frey.
If you want to express a constraint on tasks then you can do it like this
QProject.project.tasks.any().id.eq(searchTerm)
If you want to express preloading of certain tasks instead via a left join you can't express that via a Predicate. A Predicate in Querydsl is a boolean expression for the where, join-on and having parts of the query.

How do I left join tables in unidirectional many-to-one in Hibernate?

I'm piggy-backing off of How to join tables in unidirectional many-to-one condition?.
If you have two classes:
class A {
#Id
public Long id;
}
class B {
#Id
public Long id;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id")
public A parent;
}
B -> A is a many to one relationship. I understand that I could add a Collection of Bs to A however I do not want that association.
So my actual question is, Is there an HQL or Criteria way of creating the SQL query:
select * from A left join B on (b.parent_id = a.id)
This will retrieve all A records with a Cartesian product of each B record that references A and will include A records that have no B referencing them.
If you use:
from A a, B b where b.a = a
then it is an inner join and you do not receive the A records that do not have a B referencing them.
I have not found a good way of doing this without two queries so anything less than that would be great.
Thanks.
I've made an example with what you posted and I think this may work:
select a,b from B as b left outer join b.parent as a in HQL.
I have to find a "criteria" way of doing that though.
You may do so by specifying the fetch attribute.
(10) fetch (optional) Choose between outer-join fetching and fetching by sequential select.
You find it at: Chapter 6. Collection Mapping, scroll down to: 6.2. Mapping a Collection
EDIT
I read in your question's comment that you wanted a way to perform a raw SQL query? Here a reference that might possibly be of interest:
Chapter 13 - Native SQL Queries
and if you want a way to make it possible through HQL:
Chapter 11. HQL: The Hibernate Query Language
In chapter 11, you want to scroll down to 11.3. Associations and joins.
IQuery q = session.CreateQuery(#"from A as ClassA left join B as ClassB");
I guess however that ClassB needs to be a member of ClassA. Further reasdings shall help.
Another thing that might proove to be useful to you are named queries:
<query name="PeopleByName">
from Person p
where p.Name like :name
</query>
And calling this query from within code like so:
using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction()) {
session.GetNamedQuery("PeopleByName")
.SetParameter("name", "ayende")
.List();
tx.Commit();
}
Please take a look at the referenced link by Ayende who explains it more in depth.

How to build HQL query, thats joins subtables marked LAZY, automatically?

I have some entity:
public class Album extends GenericAuditedEntity {
#OneToMany(fetch = FetchType.LAZY)
private Set<Item> itemSet = new HashSet<Item>();
}
And when i run HQL like this:
em.createQuery("select a from Album a").getResults()
it produses many SQL queries:
One for select data from Album's table. Smth like this: select .... from Album_table;
And one query for each fetched row, for selecting items. Smth like this:
select .... from Item_table iwhere i.Album_id = :Album_id;
But when i run em.createQuery("
select a.id, b.id
from Album a
left join Item i
").getResults()
it produses one SQL query. But it's result is list of some parameters, that i need put into the entities manually.
How can i build HQL with joins automatically and automatically put the results to the entities? Is it possible?
You need to use join fetch:
em.createQuery("select a.id, b.id from Album a left join fetch Item i ").getResults();
Note that there are certain side effects to that, described in detail the above link.
If you are using join fetch then you don't need the IDs, you can retrieve the Entity as Hibernate will also populate the association in it's first-level cache
em.createQuery("select a from Album a left join fetch a.itemSet").getResultList();
However if you are retrieving the IDs but want populated Objects/Entities then consider using a Constructor
em.createQuery("select new com.xxx.AlbumItem(a.id, b.id) from Album a left join fetch a.itemSet b").getResultList();
dont use lazy fetching. set fetch type to eager

Categories

Resources