Subquery using CriteriaBuilder in Predicate overridden method Spring boot - spring-boot

I was trying to create one subquery in Springboot project, where I have created one custom class which is extending interface implements Specification<T> which gives overridden method #Override public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb).
Below is my SQL query which I would like to make it using CriteriaBuilder.
Select * from student_projects WHERE project_id IN (Select project_id
from student_courses where course_type = 3)
Below is the code snippet of the custom model class of implements Specification<T> which I have created,
#Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.distinct(true);
return root.join("student_courses", JoinType.LEFT).get("project_id").in("3");
}
Kindly suggest me if more information is needed. Thank you all

Related

CriteriaBuilder OffsetDateTime Comparison

I use Springboot, and I'd like to implement a "search" API for an entity that has an "OffsetDateTime" attribute.
The attribute is of type "OffsetDateTime" in my Hibernate entity :
#Column(name = "creation_date")
private OffsetDateTime creationDate;
The attribute that I get from the API is of type "OffsetDateTime" too.
I use the Springboot Specification, with the OffsetDateTime value as criteria.getValue() :
#Override
public Predicate toPredicate(Root<Job> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder builder) {
return builder.equal(root.get("creationDate"), criteria.getValue());
}
But the above code doesn't return me the entity with the specified date
However, the following code does :
#Override
public Predicate toPredicate(Root<Job> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder builder) {
Predicate predicate = builder.between(root.<OffsetDateTime>get("creationDate"),
((OffsetDateTime) criteria.getValue()).minus(1, ChronoUnit.MILLIS),
((OffsetDateTime) criteria.getValue()));
}
In my DB I have "2020-03-12 17:25:11.047", and the value I pass to the API is "2020-03-12T17:25:11.047+01:00". I'm in France, so I have a 1 hour offset, and the time is saved as a local time in the db, but that doesn't seem to be the problem as the second solution does return the entity I want.
Do you know why the first solution doesn't return me the entity, and how I could get the entity without having to set a 1 millisecond interval ?

Spring Boot JPA Specification for Compare Dates

I need to find the table records which will come between the dates passed by the user. I am trying to write a specification for this but it is showing me compile time error as below :
The method between(Expression<? extends Y>, Expression<? extends Y>, Expression<? extends Y>)
in the type CriteriaBuilder
is not applicable for the arguments (Expression<Date>, Object, Object)
I have tried search on the various forums but didn't able to get how to solve this issue,may be I am doing something wrong. Please help me on this.
Specification Class
public class ScheduleClassSpecification implements Specification<ScheduleClassInformation> {
private SearchCriteria criteria;
#Override
public Predicate toPredicate(Root<ScheduleClassInformation> root,
CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Expression<String> expression;
Predicate predicate =null;
if((criteria.getKey().equalsIgnoreCase("student"))){
expression = root.join("course").join("student").get("student");
predicate = criteriaBuilder.equal(expression, criteria.getValue());
}else if(criteria.getKey().equalsIgnoreCase("startDate") || criteria.getKey().equalsIgnoreCase("endDate")){
predicate = criteriaBuilder.between
(root.<Date>get(criteria.getKey()).as(java.util.Date.class),
criteria.getValue(),
criteria.getValue()); // Compile Time Error on this line
}
return predicate;
}
Criteria Class
public class SearchCriteria {
private String key;
private String operation;
private Object value;}
Service For Extracting the record from Repository
if(!Utility.isNull(Id)){
idSpec = new ScheduleClassSpecification(new SearchCriteria("Student",":",Id));
}
Page<SCOutput> listreturn = scRepo.findAll(Specification.where(idSpec), SCOutput.class,new PageRequest(0, 100));
As the compiler error tells you need an Expression.
You can easily do that by replacing criteria.getValue() with criteriaBuilder.literal(criteria.getValue())

How can I use Spring's pagination (using Pageable) while writing a dynamic query using QueryDSL?

I am trying to use pagination with QueryDSL - using the com.mysema.querydsl package.
All my Querydsl query types look like this -
#Generated("com.mysema.query.codegen.EntitySerializer")
public class QCountry extends EntityPathBase<Country> {...}
Currently, my repository implementation class looks something like this -
#Override
public Page<Country> findPaginatedCountries(String country, Optional<String> status, Pageable pageable) {
QCountry qCountry= QCountry.someObject;
QActiveCountry qActiveCountry = QActiveCountry.activeCountry;
JPAQuery jpaQuery = new JPAQuery(entityManager);
QueryBase queryBase = jpaQuery.from(qCountry).innerJoin(qActiveCountry).fetch()
.where(qCountry.codeLeft.country.upper().eq(country.toUpperCase()))
.where(qCountry.codeRight.country.upper().eq(country.toUpperCase()));
if(status.isPresent()){
queryBase = queryBase.where(qActiveCountry.id(qCountry.active.id))
.where(qActiveCountry.status.upper().eq(status.get().toUpperCase()));
}
.......}
Now, I want this dynamic query to return a paginated response. I want to use Spring's pagination to do that and not manually set offset, size etc.
I know I can use QueryDslRepositorySupport class - as implemented here - https://github.com/keke77/spring-data-jpa-sample/blob/master/spring-data-jpa/src/main/java/com/gmind7/bakery/employee/EmployeeRepositoryImpl.java
Sample code from the above link -
#Override
public Page<Employees> QFindByOfficeCode(long officeCode, Pageable pageable) {
//JPAQuery query = new JPAQuery(em);
JPQLQuery query = from(QEmployees.employees).where(QEmployees.employees.officeCode.eq(officeCode));
query = super.getQuerydsl().applyPagination(pageable, query);
SearchResults<Employees> entitys = query.listResults(QEmployees.employees);
return new PageImpl<Employees>(entitys.getResults(), pageable, entitys.getTotal());
}
However, to do that -
I need to pass JPQLQuery object to the applyPagination method. How can I do that without changing my code (Ofcourse, the repository class will extend QueryDslRepositorySupport class). Currently, I am using JPAQuery as you can see.
OR
I probably need to change my QueryDSL types by having them extend EntityPath instead of EntityPathBase so that I can use JPQLQuery.from() to generate the query and then use the applyPagination method, which requires a JPQLQuery object. However, my Q classes are extending EntityPathBase class instead. Should I be use com.querydsl package instead of com.mysemsa.querydsl package to generate query types?
OR
Other option is to use the following - http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/querydsl/QueryDslPredicateExecutor.html#findAll-com.querydsl.core.types.Predicate-org.springframework.data.domain.Pageable-
Code snippet below -
Page<T> page = QueryDslPredicateExecutor.findAll(org.springframework.data.querydsl.Predicate predicate, Pageable pageable)
However, I am making joins between two tables and then filtering results with a where clause (as you can see above in my code). How can I pass a predicate object in the findAll method above? Not sure how to include a join in it.
Please let me know if the problem is not clear, I can add more details.
EDIT: There is a many to one relationship between Country and ActiveCountry.
Country class has an ActiveCountry reference. And we have to do a join between both ids. Is is possible that Country can have null ActiveCountry. Therefore, we want an inner join - only non null values for active country
#ManyToOne
#JoinColumn(name="id")
ActiveCountry active;
Step 1: Annotate the entity class with #QueryEntity
#Entity
#QueryEntity
public class Country {}
This seems to have been addressed already since the question shows Q classes.
Step 2: Have the repository interface extend QueryDslPredicateExecutor
public interface CountryRepository
extends PagingAndSortingRepository<Country, Long>
, QueryDslPredicateExecutor<Country> {
}
Step 3: Invoke the Page<T> findAll(Predicate query, Pageable page) method provided by QueryDslPredicateExecutor
public Page<Country> getCountries(String country, Optional<String> status, Pageable page) {
QCountry root = QCountry.country;
BooleanExpression query = root.codeLeft.country.equalsIgnoreCase(country);
query = query.and(root.codeRight.country.equalsIgnoreCase(country));
if (status.isPresent()) {
query = query.and(root.active.status.equalsIgnoreCase(status));
}
return countryRepository.findAll(query, page);
}

How to write a findByCritera1InAndCritera2In using (spring data) CrudRepository with parameters being optional

I'm using spring boot with spring data, specifically the class PagingAndSortingRepository that extends CrudRepository.
I need a query which returns all entries of a table if matches either of the four lists ignoring it when it is null (or empty).
If I use findByTypeInAndLocaleInAndCategoryInAndTagIn and one of the lists is empty, the result is empty as well. So I ended up writing several finders and depending on which lists are empty using a different one. Is it possible to combine this in one finder?
So e.g. if I use findByTypeAndLocale I'd like to match all values of type if the list type is empty.
Happy about any hints.
#Repository
public interface FeedRepository extends PagingAndSortingRepository<FeedEntry, Long>, JpaSpecificationExecutor<FeedEntry> {
public List<FeedEntry> findByGuid(String guid);
public Page<FeedEntry> findAll(Pageable pageable);
public Page<FeedEntry> findByLocale(List<LocaleEnum> type, Pageable pageable);
public Page<FeedEntry> findByType(List<FeedTypeEnum> type, Pageable pageable);
public Page<FeedEntry> findByTypeAndLocale(FeedTypeEnum type, LocaleEnum locale, Pageable pageable);
public Page<FeedEntry> findByTypeInAndLocaleIn(List<FeedTypeEnum> type,List<LocaleEnum> locale, Pageable pageable);
public Page<FeedEntry> findByTypeInAndLocaleInAndCategoryIn(List<FeedTypeEnum> type,List<LocaleEnum> locale, List<String> category, Pageable pageable);
public Page<FeedEntry> findByTypeInAndLocaleInAndTagIn(List<FeedTypeEnum> type,List<LocaleEnum> locale, List<String> tag, Pageable pageable);
public Page<FeedEntry> findByTypeInAndLocaleInAndCategoryInAndTagIn(List<FeedTypeEnum> type,List<LocaleEnum> locale, List<String> category, List<String> tag, Pageable pageable);
}
You cannot do it as you are doing. If the list is empty means there isn't any value which matches with your condition query.
To do what you are looking for you need to do it with QBE (Query By example) which is compatible with CrudRepository
QBE doc
Why? You need a dynamic query and as the doc says:
Query by Example (QBE) is a user-friendly querying technique with a
simple interface. It allows dynamic query creation and does not
require to write queries containing field names. In fact, Query by
Example does not require to write queries using store-specific query
languages at all.
An example of the doc:
public interface PersonRepository extends JpaRepository<Person, String> { … }
public class PersonService {
#Autowired PersonRepository personRepository;
public List<Person> findPeople(Person probe) {
return personRepository.findAll(Example.of(probe));
}
}
**Making example for your case...
I'm affraid it is not possible at the moment.
IMHO the best way you can achieve your goal is to use Spring Data JPA Specifications (http://docs.spring.io/spring-data/jpa/docs/1.10.2.RELEASE/reference/html/#specifications) and manually check every parameter for not null value...

#NamedQuery override findAll in Spring Data Rest JpaRepository

Is there a way to override the findAll query executed by Spring Data Rest?
I need a way of filtering the results based on some specific criteria and it seems that using a #NamedQuery should be along the lines of what I'm looking for so I setup a test.
#Entity
#Table(name = "users")
#NamedQueries({
#NamedQuery(name = "User.findAll", query="SELECT u FROM User u WHERE u.username = 'test'"),
#NamedQuery(name = "User.findNameEqualsTest", query="SELECT u FROM User u WHERE u.username = 'test'")
})
public class User implements Serializable, Identifiable<Long> { }
With this in place I would expect SDR to utilize my findAll() query (returning 1 result) but instead it executes the same old findAll logic (returning all results).
In my Repository I added:
#Repository
#RestResource(path = "users", rel = "users")
public interface UserJpaRepository extends JpaRepository<User, Long> {
public Page<User> findNameEqualsTest(Pageable pageable);
}
and in this case it DOES pick up the provided #NamedQuery. So...
How should I go about overriding the default findAll() logic? I need to actually construct a complex set of criteria and apply it to the result set.
In the upcoming version 1.5 (an RC is available in our milestone repositories) of Spring Data JPA you can simply redeclare the method in your repository interface and annotate it with #Query so that the execution as query method is triggered. This will then cause the named query to be looked up just as you're already used to from query methods:
interface UserJpaRepository extends PagingAndSortingRepository<User, Long> {
#Query
List<User> findAll();
Page<User> findNameEqualsTest(Pageable pageable);
}
A few notes on your repository declaration:
You don't need to annotate the interface with #Repository. That annotation doesn't have any effect at all here.
Your #RestResource annotation configures the exporter in a way that will be the default anyway in Spring Data REST 2.0 (also in RC already). Ging forward, prefer #RestRepositoryResource, but as I said: the pluralization will be the default anyway.
We generally don't recommend to extend the store specific interfaces but rather use CrudRepository or PagingAndSortingRepository.
Yes, you can create your Implementation of your Repository interface, there is acouple section in
http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/repositories.html#repositories.custom-implementations
Repository
#Repository
public interface PagLogRepository extends JpaRepository<PagLogEntity, Long>, PagLogCustomRepository {
Custom Interface
public interface PagLogCustomRepository {
PagLogEntity save(SalesForceForm salesForceForm) throws ResourceNotFoundException;
Custom implementation
public class PagLogRepositoryImpl implements PagLogCustomRepository {
#Override
public PagLogEntity save(final SalesForceForm salesForceForm) throws ResourceNotFoundException {
query = emEntityManager.createNamedQuery("findItemFileByDenormalizedSku", ItemFileEntity.class);
query.setParameter("skuValue", rawSku);
Instead of override save make it with findAll, then you can create complex customization

Resources