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.
Related
I have this SQL query:
select ts.scorename from content_package cp
join content_package_content_package_components cpcps on cpcps.content_package = cp.id
join content_package_component cpc on cpc.id = cpcps.content_package_components
join tests t on t.id = cpc.assessment
join test_scores ts on ts.tests_id = t.id
where cp.tag = 'C_TS_EN_ABSA_G_'
And want to convert it to JPA, ideally Specifications - is this possible?
you can write this query in JPQL but firs you need to create POJO class of your models. if you are using Intelij idea you can create your models in it by going to persistence section ,right click on your data source and select generate persistence mapping by (hibernate or database schema). after creating models you should change your table names to pojo classes in query and so on ....
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.
I am trying to build this query with JPA Criteria API
SELECT s FROM snapshot s
INNER JOIN (
SELECT collector_id, entity_id, MAX(timestamp) AS "timestamp"
FROM snapshot GROUP BY collector_id, entity_id
) AS j ON s.TIMESTAMP = j.TIMESTAMP AND s.collector_id = j.collector_id AND s.entity_id = j.entity_id;
The inner select should get 3 properties to identify a snapshot, and then the outer select will get all the other properties of a snapshot based on 3 that inner select returned.
I have success with building the inner select, but how to combine the outer select with the inner using a join?
Or, maybe, there is a different way to construct the query itself in a way, that doesn't include a sub query...
EDIT:
Similar quertion: jpa criteria-api: join with subselect
JPA does not support sub-selects in the FROM clause. Some JPA providers may support this.
For example EclipseLink does:
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Sub-selects_in_FROM_clause
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.
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.