Criteria Builder In query for list of string - spring

I am passing following json from front end :
{names: 'ABC MKL-56-2,ABC MKL-56-3'};
In service layer,I am trying to run in query with the help of criteria builder as follows :
public List<APDetails> getWP(String names) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<APDetails> query = builder.createQuery(APDetails.class);
Root<APDetails> root = query.from(APDetails.class);
Predicate hasA = builder.in(root.get(APDetails_.names).in(Arrays.asList(names.split(","))));
query.where(builder.and(hasA));
List<APDetails> APs = em.createQuery(query.select(root)).getResultList();
return APs;
}
I am getting following error :
Error message: org.hibernate.hql.internal.ast.QuerySyntaxException:
unexpected token: in near line 1, column 163 [select generatedAlias0
from com.app.ow.APDetails as generatedAlias0 where generatedAlias0.names in (:param0, :param1) in ()]

First of all, if you're using springboot, I suggest you extend the JpaSpecificationExecutor class (check here, here, and here for more information) from your APDetailsRepository (I believe you're using them somewhere...):
public interface APDetailsRepository extends JpaRepository<APDetails, Long>, JpaSpecificationExecutor<APDetails> {
Then, try this:
#Autowired
public APDetailsRepository apDetailsRepository;
........
public List<APDetails> getWP(String names) {
List<String> namesAsList = Arrays.asList(names.split(","));
List<APDetails> listAPDetails = this.apDetailsRepository.findAll(createSpecification(namesAsList));
return listAPDetails;
}
public Specification<APDetails> createSpecification(List<String> names) {
return new Specification<APDetails>() {
private static final long serialVersionUID = 1L;
#Override
public Predicate toPredicate(Root<APDetails> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<Predicate>();
if (names!= null && !names.isEmpty()) {
List<Predicate> predicatesNames = new ArrayList<Predicate>();
for (String name : names) {
predicatesNames.add(builder.equal(root.<String>get("names"), name));
//I believe that the "APDetails_.names" attribute is a String...
}
predicates.add(builder.or(predicatesNames.toArray(new Predicate[] {})));
}
return builder.and(predicates.toArray(new Predicate[] {}));
}
};
}

Related

Spring Jpa Specification with list of long where in query

I want to run a where in query with Spring JPA Specification and criteria builder. I am having issue where I will receive a List ids from request and run specification query but could't find any way to do so.
This is what I have done so far.
public class DistributorMasterDataSpecification implements Specification<DistributorMasterData> {
#Override
public Predicate toPredicate(Root<DistributorMasterData> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return
codeSpec()
.and(idSpec())
.toPredicate(root, query, criteriaBuilder);
}
private Specification<DistributorMasterData> idSpec() {
return ((root, query, criteriaBuilder) ->
Objects.isNull(filterDto.getDistributorIds()) ?
null : root.get(DistributorMasterData_.ID).in(filterDto.getDistributorIds())
);
}
}
I am unable to figure out how to prepare where in query with specification. With current implementation I facing the error below
org.springframework.dao.InvalidDataAccessApiUsageException: literal value cannot be null; nested exception is java.lang.IllegalArgumentException: literal value cannot be null
You can create a custom repository and after you do the implementation
public interface EditeurRepositoryCustom {
Page<User> search(UserSearch search, Pageable page);
}
#Repository
public class UserRepositoryCustomImpl extends SimpleJpaRepository<User,Long> implements EditeurRepositoryCustom {
#Autowired
private EntityManager entityManager;
public EditeurRepositoryCustomImpl(EntityManager em) {
super(User.class, em);
}
public Page<User> search(UserSearch search, Pageable page) {
Specification<User> hasExternalOrder = (Root<User> mainRoot, CriteriaQuery<?> mainCq, CriteriaBuilder mainCb) -> {
return mainRoot.get("externalOrder").isNotNull();
};
Specification<User> isPrincipal = (Root<User> mainRoot, CriteriaQuery<?> mainCq, CriteriaBuilder mainCb) -> {
if(!search.principal()){
return mainCb.and();
}
return mainCb.isTrue(mainRoot.get("main"));
};
};
return findAll(hasNameContain.and(hasExternalOrder).and(isPrincipal), page);
}
You can combine multiple spec

Wrong Pagination info using JpaSpecifications

I am using spring boot 2.2.6
I am implementing an API that has the option of filtering data and it must be returned paginated
so I used specifications (JpaSepecificationExecutor) and the specification has a join with another table
and this join is mandatory as a customerId is always given and added to predicate list
the problem is that the paginated info returned is wrong mainly (totalElements) and (totalPages)
this is the specifications class
`
#Data
#AllArgsConstructor
#NoArgsConstructor
public class CustomerSalesOrderSpecification implements Specification<SalesOrder> {
#NonNull
private Integer customerId;
private Integer orderId;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate purchasedDateFrom;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate purchasedDateTo;
private String customerFirstname;
private BigDecimal baseGrandTotalFrom;
private BigDecimal baseGrandTotalTo;
private String storeName;
#Override
public Predicate toPredicate(#NonNull Root<SalesOrder> root, #NonNull CriteriaQuery<?> query,
#NonNull CriteriaBuilder builder) {
List<Predicate> predicateList = new ArrayList<>();
predicateList.add(customerId(customerId).toPredicate(root, query, builder));
if (Objects.nonNull(orderId)) {
predicateList.add(orderId(orderId).toPredicate(root, query, builder));
}
if (Objects.nonNull(purchasedDateFrom)) {
predicateList.add(purchaseDateFrom(purchasedDateFrom).toPredicate(root, query, builder));
}
if (Objects.nonNull(purchasedDateTo)) {
predicateList.add(purchaseDateTo(purchasedDateTo).toPredicate(root, query, builder));
}
if (Objects.nonNull(customerFirstname)) {
predicateList.add(customerFirstname(customerFirstname).toPredicate(root, query, builder));
}
if (Objects.nonNull(baseGrandTotalFrom)) {
predicateList.add(baseGrandTotalFrom(baseGrandTotalFrom).toPredicate(root, query, builder));
}
if (Objects.nonNull(baseGrandTotalTo)) {
predicateList.add(baseGrandTotalTo(baseGrandTotalTo).toPredicate(root, query, builder));
}
if (Objects.nonNull(storeName)) {
predicateList.add(storeName(storeName).toPredicate(root, query, builder));
}
return builder.and(predicateList.toArray(new Predicate[]{}));
}
public Specification<SalesOrder> customerId(Integer customerId) {
return (root, query, builder) -> {
Join<SalesOrder, CustomerEntity> salesOrderCustomer = root.join("customerEntity");
return builder.equal(salesOrderCustomer.get("entityId"), customerId);
};
}
public Specification<SalesOrder> orderId(Integer orderId) {
return ((root, query, builder) -> builder.equal(root.get("entityId"), orderId));
}
public Specification<SalesOrder> purchaseDateFrom(LocalDate purchasedDateFrom) {
return (root,query,builder) -> builder.or(
builder.greaterThan(root.get("createdAt"),Timestamp.valueOf(purchasedDateFrom.atStartOfDay())),
builder.between(root.get("createdAt"),Timestamp.valueOf(purchasedDateFrom.atStartOfDay()),
Timestamp.valueOf(purchasedDateFrom.atTime(23,59))));
}
public Specification<SalesOrder> purchaseDateTo(LocalDate purchasedDateTo) {
return (root,query,builder) -> builder.or(
builder.lessThan(root.get("createdAt"),Timestamp.valueOf(purchasedDateTo.atStartOfDay())),
builder.between(root.get("createdAt"),Timestamp.valueOf(purchasedDateTo.atStartOfDay()),
Timestamp.valueOf(purchasedDateTo.atTime(23,59))));
}
public Specification<SalesOrder> customerFirstname(String customerFirstname) {
return ((root, query, builder) -> builder.like(root.get("customerFirstname"), "%" + customerFirstname + "%"));
}
public Specification<SalesOrder> baseGrandTotalFrom(BigDecimal baseGrandTotalFrom) {
return ((root, query, builder) -> builder.greaterThanOrEqualTo(root.get("baseGrandTotal"), baseGrandTotalFrom));
}
public Specification<SalesOrder> baseGrandTotalTo(BigDecimal baseGrandTotalTo) {
return ((root, query, builder) -> builder.lessThanOrEqualTo(root.get("baseGrandTotal"), baseGrandTotalTo));
}
public Specification<SalesOrder> storeName(String storeName) {
return ((root, query, builder) -> builder.like(root.get("storeName"), "%" + storeName + "%"));
}
}
`
for example if I filter based on id and name I get totalElements 20 when the real number is 377
and totalPages is 1 which is wrong of course
I tried writing the whole query in JPQL and it worked correctly so the issue is only I use specifications
with pagination and a join
I checked similar questions, but they were related to an error when using join fetch, I am using a normal join and there is no error just wrong pagination info in response
what might be the reason ?

How to write a generic java function in Spring-boot for querying results,based on multiple query filters

I was working on java using jdo where I used to write query functions like below, which queries from an Entity based on what parameters are passed to the function.
Now Im moving to spring-boot, and want to know if I can achieve the same using spring-boot.Any help or suggestions would be heartfully appreciated.Thank you!!
public List<Result> getQueryResult(int filter1, String filter2,Float filter3,Long id){
Query query = new Query("select from Entity1");
String filter = "id == "+id;
if(filter1 != null){
filter = filter+" && filter1 == "+filter1+";
}
if(filter2 != null){
filter = filter+" && filter2 == '"+filter2+"'";
}
if(filter3 != null){
filter = filter+"filter3 == "+filter3;
}
query.setFIlter(filter);
List<Result> results = query.excute();
return results;
}
You have two options - you can use JPA Criteria Builder or JPA Specifications
class Person {
String firstName;
String lastName;
int age;
}
JPA Criteria Builder
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Person> query = builder.createQuery(Person.class);
Root<Person> root = query.from(Person.class);
Predicate sellAlcohol = builder.ge(root.get(Person_.age), 21);
Predicate toMindy = builder.equal(root.get(Person_.firstName), "Mindy");
Usage
query.where(builder.and(sellAlcohol, toMindy));
em.createQuery(query.select(root)).getResultList();
Specificatons
public PersonSpecifications {
public static Specification<Person> sellAlcohol() {
return new Specification<Person> {
public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
return cb.ge(root.get(Person_.age), 21);
}
};
}
public static Specification<Person> toMindy() {
return new Specification<Person> {
public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
return cb.equal(root.get(Person_.firstName), "Mindy");
}
};
}
}
Usage
personRepository.findAll(where(sellAlcohol()).and(toMindy()));

spring jpa query with pageable, sort and filter and return projection

I am using Spring Data Rest with org.springframework.boot 1.5.2 with hibernate 5.2.9. What i am trying to achieve is a way to use JPA to query with sort, filter, pageable that can return a subset of the entity or return a projection.
Below is the code that uses:
(1) Specification for filtering
(2) Projection and Excerpts to apply projection in collection
(3) The controller that tries to return Page,
but it only works if the return type is Page.
where Student is the entity, StudentLite is the projection
Question is:
(1) How to have a query+sort+filter that returns Page projection
(2) Possible to apply the Excerpts to just that query?
(3) Any way to use #JsonView in #RepositoryRestController to solve?
StudentRepository class
#RepositoryRestResource(excerptProjection = StudentLite.class)
public interface StudentRepository extends PagingAndSortingRepository<Student,Long>,
JpaSpecificationExecutor<Student> {}
and
StudentSpecification class
public class StudentSpecification {
public static Specification<Student> filteredStudentList(StudentSearch c) {
final StudentSearch criteria = c;
return new Specification<Student>() {
#Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Join<Student, Contact> joinContact = root.join(Student_.contact);
Path<Contact> contact = root.get(Student_.contact);
Path<String> officialId = root.get(Student_.officialId);
Path<String> name = root.get(Student_.name);
Path<String> email = contact.get(Contact_.email);
Path<String> phoneMobile = contact.get(Contact_.phoneMobile);
final List<Predicate> predicates = new ArrayList<Predicate>();
if(criteria.getOfficialId()!=null) {
predicates.add(cb.like(officialId, "%" + criteria.getOfficialId() + "%"));
System.out.println("==not null...criteria.getOfficialId()="+criteria.getOfficialId()+" :officialId="+officialId.toString());
}
if(criteria.getName()!=null) {
predicates.add(cb.like(name, "%"+criteria.getName()+"%"));
}
if(criteria.getEmail()!=null) {
predicates.add(cb.like(email, "%"+criteria.getEmail()+"%"));
}
if(criteria.getPhoneMobile()!=null) {
predicates.add(cb.like(phoneMobile, "%"+criteria.getPhoneMobile()+"%"));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
}
}
and the controller where the class is annotated with #ExposesResourceFor(Student.class) and #RepositoryRestController :
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody Page<StudentLite> getStudentList(Pageable pageable, #RequestParam Map<String,String> criteria) {
StudentSearch ss = new StudentSearch(criteria);
// Below statement fail, as findAll(...) is suppose to return Page<Student>
Page<StudentLite> pagedStudentLite = studentRep.findAll( StudentSpecification.filteredStudentList(ss), pageable);
return pagedStudentLite;
}

Spring Specification Criteria Multiple Joins ? How?

I got stuck using a Spring Project with Spring Data + specification + criteria api.
I will try to simulate the situation with general entities we used write to get easy example.
The Entities:
Consider all attributes of the each entity is passed on the constructor showed below
Country(Long id, String name, String iso)
State(Long id, String name, String iso)
City(Long id, String name, String iso)
This is my repository:
public interface CityRepository extends PagingAndSortingRepository<City, Integer>, JpaSpecificationExecutor<City> {
}
As you can see, I don't need to implement anything on the repository
This is my service
#Service
#Transactional
public class CityService {
#Autowired
private CityRepository cityRepository;
#Transactional(readOnly = true)
public CityListVO findByNameLike(String name, PageRequest pageRequest) {
name = "%" + name + "%";
if (pageRequest == null) {
List<City> result = cityRepository.findAll(fillGridCriteria(name));
return new CityListVO(1, result.size(), result);
} else {
Page<City> result = cityRepository. findAll(fillGridCriteria(name), pageRequest);
return new CityListVO(result.getTotalPages(), result.getTotalElements(), result.getContent());
}
}
private static Specification<City> fillGridCriteria(String name) {
return new Specification<City>() {
#Override
public Predicate toPredicate(
Root<City> root,
CriteriaQuery<?> query,
CriteriaBuilder builder) {
/*
The current return I can do a like by name, and it works fine.
My problem is if for any reason I need to do multiple joins like the folow jpql:
select ci FROM City ci, State st, Country co where ci.st = st AND st.co = co AND co.name = 'Canada';
How to do this from here ? Inside this method.
How is gonna be the return for this method ?
*/
return builder.like(root.get("name"), name.trim());
}
};
}
}
Let's assume you want all the cities that their country's name like name and you have a relational Model in which :
Country(Long id, String name, String iso)
State(Long id,Long country, String name, String iso)
City(Long id, Long state, String name, String iso)
Predicate:
private static Specification<City> fillGridCriteria(String name) {
return new Specification<City>() {
#Override
public Predicate toPredicate(
Root<City> root,
CriteriaQuery<?> query,
CriteriaBuilder builder) {
return
builder.like(root.get("state").get("country").get("name"), name.trim());
}
};
}

Resources