JPA Criteria - fetch with where clause - java

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.

Related

How to remove all SubcriteriaList from projectionCriteria in Hibernate (Java) - prevent from using left join

I am working with Hibernate and dto,dao design patterns (Java).
i have an entity class and attribute in it and they are defined with #ManyToOne annotation.
I would like to create a count query and "tell" hibernate "DO NOT JOIN" with #ManyToOne tables
While creating a count query:
(Long) crit.setProjection(Projections.count("id")).uniqueResult();
The sql exceute by hibernate is with left join in it .
Even if i count explicitly on the #Id annotation from the entity class .
The actual query appear is with LEFT JOIN for all the "other tables".
That SQL query build by Hibernate - is not efficient since there is no reason for creating a left join when #ManyToOne is set.
After trying and reading about hibernate i found out about FetchMode
but even when setting FetchMode.LAZY
.setFetchMode("brand", FetchMode.LAZY)
The Sql from hiberante having left join in it.
i have also attached the photo from debug that showing all the SubcriteriaList
which is under projectionCriteria .
How could i tell Hibernate DON'T left join Tables from entity class ?
(without writing SQL query by myself) ?
public Response findAll() {
Criteria crit = getDtoCriteria();
}
public Criteria getDtoCriteria() {
return getDtoCriteria(getDtoClass(), getSession());
}
public Criteria getDtoCriteria(Class clazz, Session session) {
Criteria crit = createEntityCriteria(session);
setProjecttionForDto(crit, true, clazz);
return crit;
}
This all are my Subcriteria
[Subcriteria(bran*******ance:bran*******ance), Subcriteria(buc*****:buc*****), Subcriteria(br****:br****), Subcriteria(dyn***:dyn***), Subcriteria(dyna*****.user:user)]
Try using FetchMode.SELECT instead:
(Long) crit.setFetchMode("brand",FetchMode.SELECT)
.setProjection(Projections.count("id"))
.uniqueResult();

Inner joins using Hibernate Criteria on non-primary key column

I want to write this query using Hibernate Criteria language. I am pretty new to Hibernate and not able to convert this query into Criteria form. I referred lots of answers available on SO but in my case I am using inner join on different columns rather than primary key/ foreign key column. I referred this but still can't make it right.
select TableA.columnA1, TableA.columnA2, TableA.columnA3, TableB.columnB1, TableC.columnC2 from TableA inner join TableB
on
cast(TableA.columnA3 as Integer) = TableB.columnB2
inner join
TableC
on
TableB.columnB3 = TableC.columnC1
To handle the joining logic, you are going to want to use from for each of the tables and include all of your conditions from the on-clauses in the where predicate.
Here is a JPA example that handles a parent-child relationship without having a foreign-key relationship:
EntityManager em = getDb().getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Child> criteria = cb.createQuery(Child.class);
Root<Parent> p = criteria.from(Parent.class);
Root<Child> c = criteria.from(Child.class);
Predicate condition = cb.and(
cb.equal(c.get(Child_.parentId), p.get(Parent_.id)),
...
);
criteria.where(condition);
criteria.select(c);
criteria.orderBy(cb.asc(c.get(Child_.createDate)));
TypedQuery<Child> q = em.createQuery(criteria).setMaxResults(limit);
A JPA example is provided here, because the Hibernate criteria API is deprecated in favor of the JPA criteria API (see Legacy Hibernate Criteria Queries).

JPA CriteriaBuilder - Change default join type

I'd like to change the default join type from the criteriaBuilder from INNER to LEFT. Is this possible in some way? Atm I'm doing things like:
Join<Request, Ship> requestShipJoin = requestRoot.join("ship", JoinType.LEFT);
Join<Ship, Visit> shipVisitJoin = requestShipJoin.join("visit", JoinType.LEFT);
I've got a lot of nullable foreign keys, but if the FK is null the result still has to return (used with ordening). I'm hoping that this is possible since all the joins that I have to do are of type LEFT.

JPA Hibernate Generated Left Join ignored using criteria builder

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.

How to join when building Predicate(s), but without having a JPAQuery instance

When building a Predicate for the Entity Book, I would like to be able
to left join on Category (ManyToMany) to add a AND Predicate on Category.
I could simply achieve that if I have the JPAQuery instance :
if (catId != null) {
jpaQuery.leftJoin(book.categories, category);
jpaQuery.where(category.id.eq(catId).or(category.parentCategory.id.eq(catId)));
}
But when building Predicates I don't have yet the JPAQuery.
So for the Predicate itself I could do :
booleanBuilder.and(category.id.eq(this.categoryId).or(category.parentCategory.id.eq(this.categoryId)));
But for the leftjoin how to proceed without the jpaQuery instance ?
You need the Query to declare the left join in Querydsl. If this is Spring Data related, they might come up with an API level solution.
book.categories.any() can be used instead of category, but it is serialized differently to JPQL, with a subquery instead of a join.

Categories

Resources