JPA Join Multiple Columns ON Clause - java

I am using JPA 2.2.0 in my project. I have below requirement to write a hibernate query using "CriteriaBuilder". Below is sample code snippet
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<A> criteriaQuery = criteriaBuilder.createQuery(A.class);
Root<A> aRoot= criteriaQuery.from(A.class);
Join<A,B> abJoin= aRoot.join("aliasB", JoinType.LEFT);
....
I want to join two tables A and B. However I want to join A with B with 2 columns c1 and c2. Is it possible in JPA 2.2.0? If so, how we can do this?
for Eg.,
I need below query,
SELECT * FROM A LEFT JOIN
B ON A.c1 = B.c1 AND **B.c2 = 'Yes'**

Related

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 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.

JPA Criteria query group by uses only the id

This is a sample entity:
public class Account{
#Id
Long id
Double remaining;
#ManyToOne
AccountType type
}
public class AccountType{
#Id
Long id;
String name;
}
Now i create a criteria query with Join as follwing :
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createquery();
Root<Account> accountRoot = criteriaQuery.from(Account.class);
Join<Account, AccountType> typeJoin = accountRoot.join(Account_.type);
criteriaQuery.multiSelect(
typeJoin,
criteriaBuilder.sum(accountRoot.get(Account_.remaining))
);
criteriaQuery.groupBy(typeJoin);
Query query = getEntityManager().createQuery(criteriaQuery);
query.getResultList();
The above code generate Sql command like following:
select accType.id, accType.name, sum(acc.remaining)
from account acc join accType on acc.accounttype_id = accType.id
group by accType.id
Above code work in PosgreSQL but can't run in Oracle, because in it select accType.name that doesn't appear in the group by clause.
update :
I think my question isn't clear for you. My question isn't about PostgreSQL or Oracle behavior in group by. My question is this :
I use typeJoin in group by clause(this means I expect hibernate use all field of AccountType in group by), but why hibernate just use identity field on group by? if I will use just identity field in group by then I can use the following statement :
criteriaQuery.groupBy(typeJoin.get(AccountType_.id)) ;
JPA/Hibernate doesn't automatically include all entity properties in a group by clause, so you have to manually specify them:
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root<Account> accountRoot = criteriaQuery.from(Account.class);
Join<Account, AccountType> typeJoin = accountRoot.join(Account_.type);
criteriaQuery.multiSelect(
typeJoin.get("id"),
typeJoin.get("name"),
criteriaBuilder.sum(accountRoot.get(Account_.remaining))
);
criteriaQuery.groupBy(typeJoin.get("id"), typeJoin.get("name"));
Query query = getEntityManager().createQuery(criteriaQuery);
query.getResultList();
If using GROUP BY, Oracle requires every column in select list to be in the GROUP BY.PostgreSQL is the same, except when grouping by the primary key, then it allows you to select any column.
From Oracle docs
In a query containing a GROUP BY clause, the elements of the select
list can be aggregate functions, GROUP BY expressions, constants, or
expressions involving one of these.
From PostgreSQL docs
When GROUP BY is present, or any aggregate functions are present, it
is not valid for the SELECT list expressions to refer to ungrouped
columns except within aggregate functions or when the ungrouped column
is functionally dependent on the grouped columns, since there would
otherwise be more than one possible value to return for an ungrouped
column. A functional dependency exists if the grouped columns (or a
subset thereof) are the primary key of the table containing the
ungrouped column.

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.

hibernate and jpa weirdness; unexpected lazy fetching

I'm using a rather old version of Hibernate (3.2.4) so it's possible this is related to that. Unfortunately the project requirements prevent me from upgrading.
I have a class Foo with many-to-one association to Bar. The association is flagged:
lazy="false" fetch="join"
Now when I do something like:
em.find(Foo.class, id);
I get the expected result: a single statement joining the FOO table with the BAR table. However, when I try something like:
em.createQuery("select f from Foo where f.id = :id")
.setParameter("id", id)
.getSingleResult();
I get the single join followed by an additional select query against BAR. The second query seems to be entirely superfluous; all the data needed to eagerly populate an instance of Foo should have been available from the initial join. It looks roughly like this:
select f.id, f.xyz, ..., b.id, b.xyz, ...
from foo f
join bar b on b.id = f.bar_id
where f.id = ?
select b.id, b.xyz, ...
from bar b
where b.id = ?
Any thoughts?
Hibernate doesn't respect fetch = "join" when executing HQL queries. From the documentation:
The fetch strategy defined in the mapping document affects:
retrieval via get() or load()
retrieval that happens implicitly when an association is navigated
Criteria queries
HQL queries if subselect fetching is used
In the case of HQL queries you have to use left join fetch:
em.createQuery("select f from Foo f left join fetch f.bar where f.id = :id")
.setParameter("id", id)
.getSingleResult();

Categories

Resources