Spring Data JPA Specification FETCH JOIN Issue - java

I am trying to create below query using spring jpa specification but it is giving error.
SELECT x FROM Magazine x join fetch x.articles a join a.publishers p WHERE p.title = 'JDJ'
Invalid path: 'generatedAlias2.title'
problem here is I am trying to do something like below
return (r, cq, cb) -> {
Join join1 = (Join) root.fetch("articles");
Join join2 = join1.join("publisher");
return cb.equal(join2.get("title"), title);
};
Is it possible to create query. Please help.

Related

Get data from join table hibernate many to many

I have this diagram:
table diagram
and I want to filter by the employees that have a project.
In normal SQL I will go like this
select * from employees e
join employee_projects ep on ep.employee_id = e.id
How can I achieve the same with Hibernate?
I tried using criteria builder and specifications but I can't get the data from the join table.
You can select all employees that have a project like this
em.createQuery(
"SELECT e FROM Employee e JOIN e.projects p WHERE p IS NOT NULL", Employee.class).getResultList()
You can join tables using join method of root object.
try something like this below
I have used it for one to many relation
Join<Post, Tag> join = root.join("tags", JoinType.INNER);
Predicate tagPredicate = criteriaBuilder.like(join.get("name"), "%" + search + "%");
for many to many relation,
Join<Post, Tag> postTagsTable = root.join("tags", JoinType.INNER);
return postTagsTable.<String>get("name").in(tagNames);
here I have tags field in Post entity, which is used inside join

Convert SQL Query to Criteria Query in Spring Boot

I'm relatively new to Spring JPA CriteriaQuery. Im trying to convert my old native query in my program to criteria query and haven't been successful on join query for multiple table with conditions. I need help converting native SQL query into Criteria Query for these query below :
select * from student s inner join (
select distinct on (student_id) * from class where status = 'Active' order by
student_id,date_register desc) c on s.id = c.user_id
inner join teacher t on t.subject_id = c.subject_id
where t.status = 'Active' and s.status='Active' order by s.name desc
Update :
Below code is as far as I can go cause I dont really know much. Am i in the right direction? I'm opting for Expression because i dont know how to use Join.
CriteriaQuery<Student> query = cb.createQuery(Student.class);
Root<Student> sRoot= query.from(Student.class);
query.select(sRoot);
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Class> cRoot= subquery.from(CLass.class);
Expression<Integer> max = cb.max(cRoot.get("dateRegister"));
subquery.select(max);
subquery.groupBy(cRoot.get("student"));
query.where(
cb.and(
cb.in(cRoot.get("dateRegister")).value(subquery)
)
);
Thanks in advance!

Spring data jpa multiple table join with subquery

This is SQL Query I have tried to join all the tables using Spring Data JPA. Some please help me out.
SELECT op.id AS "Profile ID",
op.organization_name AS "Organization name",
pav.annotation AS "legacy-sams-acc-id",
ip.subnet AS "CIDR IP range"
FROM profile.profile p
JOIN profile.organization_profile op ON op.id = p.id
JOIN profile.profile_annotation_type pat ON pat.publisher_id = p.supplier_publisher_id
LEFT OUTER JOIN
(SELECT pa.id AS id,
pa.profile_id AS profile_id,
ftpa.annotation AS annotation,
ftpa.free_text_profile_annotation_type_id AS free_text_profile_annotation_type_id
FROM profile.profile_annotation pa
JOIN profile.free_text_profile_annotation ftpa ON ftpa.id = pa.id
) AS pav ON pav.free_text_profile_annotation_type_id = pat.id AND pav.profile_id = p.id
LEFT OUTER JOIN profile.ip_range_identifier ip ON ip.organization_profile_id = op.id
WHERE pat.description = 'legacy-sams-acc-id'
AND p.supplier_publisher_id = 66
ORDER BY op.id ASC
Following will be the corresponding Java class. I'm struck with converting SQL to Java.
public Specification<OrganizationProfile> organizationProfileReportCriteria(Long publisherId) {
return (root, criteriaQuery, criteriaBuilder) -> {
Subquery<Profile> profileSubquery = criteriaQuery.subquery(Profile.class);
Root<Profile> profileRoot = profileSubquery.from(Profile.class);
Join<Profile, OrganizationProfile> profileOrganizationProfileJoin = profileRoot.join("supplierPublisher");
profileSubquery.select(profileRoot)
.where(criteriaBuilder.equal(profileOrganizationProfileJoin, root),
criteriaBuilder.equal(profileRoot.get("supplierPublisher").get("id"), publisherId));
Subquery<ProfileAnnotationType> profileAnnotationTypeSubquery = criteriaQuery.subquery(ProfileAnnotationType.class);
Root<ProfileAnnotationType> profileAnnotationTypeRoot = profileAnnotationTypeSubquery.from(ProfileAnnotationType.class);
Join<ProfileAnnotationType, OrganizationProfile> organizationProfileAnnotationTypeJoin
= profileAnnotationTypeRoot.join("publisher");
profileAnnotationTypeSubquery.select(profileAnnotationTypeRoot)
.where(criteriaBuilder.equal(organizationProfileAnnotationTypeJoin, root),
criteriaBuilder.equal(profileAnnotationTypeRoot.get("publisher").get("id"), publisherId));
Subquery<ProfileAnnotation> profileAnnotationSubquery = criteriaQuery.subquery(ProfileAnnotation.class);
Root<ProfileAnnotation> profileAnnotationRoot = profileAnnotationTypeSubquery.from(ProfileAnnotation.class);
Join<ProfileAnnotation, OrganizationProfile> profileAnnotationOrganizationJoin
= profileAnnotationRoot.join("profile");
profileAnnotationSubquery.select(profileAnnotationRoot)
.where(criteriaBuilder.equal(profileAnnotationOrganizationJoin, root),
criteriaBuilder.equal(profileAnnotationRoot.get("profile").get("supplierPublisher").get("id"), publisherId));
return criteriaBuilder.or(criteriaBuilder.exists(profileSubquery));
};
}
Basically, I'm not sure how to join multiple tables using JPA. Someone, please explain to me how to convert multiple join and Subquery for converting SQL to Java. Basically confusion about profile and Organization profile table.

JPA Criteria Query for Left joins

I am new to JPA and I have a Left Join scenario.
I have my native sql query as below and I am using left join to fetch the complete records from V_MONITORING table for st.id = 10001.
I have some null values for id_legislature which also needs to be selected and hence using a left join
select distinct
mv.id_set, mv.id_travel, mv.id_legislature
from
V_MONITORING mv
left join v_set st on mv.id_set = st.id
left join v_travel fg on mv.id_travel = fg.id
left join v_legislature gg on mv.id_legislature = gg.id;
The same thing I am implementing using the JPA criteria query, I am unable to fetch the null records
Below is the code
Predicate predicate = cb.conjunction();
Root<MonitoringBE> mvRoot = criteriaQuery.from(MonitoringBE.class);
mvRoot.fetch(MonitoringBE.id_set, JoinType.LEFT);
mvRoot.fetch(MonitoringBE.id_travel, JoinType.LEFT);
mvRoot.fetch(MonitoringBE.id_legislature, JoinType.LEFT);
final Path<Object> serieneinsatzterminPath= mvRoot.get(MonitoringBE.id_set);
predicate = cb.and(predicate, cb.EqualTo(serieneinsatzterminPath.get(SetBE.GUELTIG_VON_DATUM), startSetDate));
criteriaQuery.multiselect(
mvRoot.get(MonitoringBE.id_travel).alias("id_travel"),
mvRoot.get(MonitoringBE.id_set).alias("id_set"),
mvRoot.get(MonitoringBE.id_legislature).alias("id_legislature"))
.distinct(true).where(predicate);
Can some one guide me.
You have to replace the mvRoot.get(MonitoringBE.id_travel) and others in the multiselect-statement with mvRoot.join(MonitoringBE.id_travel, JoinType.LEFT). Otherwise they will end up in a inner join.

Is it possible to use Spring JPA Data Pagination with a query returning values from 2 different tables? [duplicate]

I am trying to use HQL fetching my entity along with sub-entities using JOIN FETCH, this is working fine if I want all the results but it is not the case if I want a Page
My entity is
#Entity
#Data
public class VisitEntity {
#Id
#Audited
private long id;
.
.
.
#OneToMany(cascade = CascadeType.ALL,)
private List<VisitCommentEntity> comments;
}
and because I have millions of visits I need to use Pageable and I want to Fetch the comments in a single database query like :
#Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);
That HQL call throws the following exception:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.ro.lib.visit.entity.VisitEntity.comments,tableName=visitdb.visit_comment,tableAlias=comments1_,origin=visitdb.visit visitentit0_,columns={visitentit0_.visit_id ,className=com.ro.lib.visit.entity.VisitCommentEntity}}] [select count(v) FROM com.ro.lib.visit.entity.VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and (v.actualArrival > :date or v.arrival > :date)]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1374)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:309)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
and once I remove the paging everything works fine
#Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public List<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...);
Obviously the problem is the count query from Spring-Data, but how can we fix it?
The easiest way is to use the countQuery attribute of the the #Query annotation to provide a custom query to be used.
#Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments …",
countQuery = "select count(v) from VisitEntity v where …")
List<VisitEntity> getVenueVisits(#Param("venueId") long venueId, …);
Alternatively in newest versions of Spring (supporting JPA 2.1 specification) you can use entity graph like this:
#EntityGraph(attributePaths = "roles")
#Query("FROM User user")
Page<User> findAllWithRoles(Pageable pageable);
Of course named entity graphs work as well.
You have to specify countQuery param for #Query and now you can use Page or List as return value.
#Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ...",
countQuery = "SELECT count(v) FROM VisitEntity v LEFT JOIN v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);
If you want completely control your query build by Specification with join fetch you can check CriteriaQuery return type and change join fetch logic according to query type like this:
public class ContactSpecification implements Specification<Contact> {
#Override
public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if(query.getResultType() == Long.class) {
root.join(Contact_.company);
} else {
root.fetch(Contact_.company);
}
return cb.equal(root.get(Contact_.company).get(Company_.name), "Company 123");
}
}
I was not able to find this info in documentation, but from SimpleJpaRepository.getCountQuery() method you can see query for count request first build for Long return type, and later fetch for expected class is running.
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
It can be not reliable since it is an implementation details which can be changed, but it works.
Try countProjection
#Query(value="SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." ,
countProjection = "v.id")
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);

Categories

Resources