I am using JpaRepository Pageable query for pagination. All the things are working fine except the sort field case sensitive issue. Following query is used for getting list.
Pageable pageable = null;
if (paginationRequestDTO.getSortOrder().equalsIgnoreCase("desc"))
pageable = new PageRequest(page, size, Sort.Direction.DESC, sortfiled);
else
pageable = new PageRequest(page, size, Sort.Direction.ASC, sortfiled);
Page<Audi> audiPage = null;
audiencePage = audiRepository.search(paginationRequestDTO.getSearchKey(), pageable);
Audi table values are: apple,az,Ajay,Bala.
when i search with sortorder of asc and sort field name,
original output : Ajay,Bala,apple,az.
Expected output: Ajay,apple,az,Bala.
I am using mysql database. table engine - Innodb,characterst-utf8,collate-utf8_bin.
Please note that its not duplicate question.i didn't get exact answer for this question.thanks in advance.
Edited: as harsh pointed out correctly, this needs to be solved on database level, using correct collation. This is important, because you probably want to have an index on the sort column for best performance.
But there are other use cases, which could combine filtering together with sorting by something other, than a pure column value, e.g. by length of description, sum or average of a column, etc. For that reason, I am including a JPA solution:
I struggled with this recently and I am afraid, that the Pageable interface does not support this out of box.
The solution was to use EntityManager, CriteriaBuilder, CriteriaQuery, Specification and implement the paging manually. You can find the solution here.
You need to construct the Page object manually:
public Page<Audi> getPage(int pageNumber, int pageSize, String descriptionFilter, Sorting sorting) {
return new PageImpl<>(
getPageItems(pageNumber, pageSize, descriptionFilter, sorting),
PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.ASC, sorting.name())),
getTotalCount(descriptionFilter)
);
}
getPageItems selects the page using LIMIT and OFFSET
private List<Audi> getPageItems(int pageNumber, int pageSize, String descriptionFilter, Sorting sorting) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Audi> query = cb.createQuery(Audi.class);
Root<Audi> root = query.from(Audi.class);
query.where(createSpecification(descriptionFilter).toPredicate(root, query, cb));
if (sorting.equals(Sorting.descriptionCaseInsensitive)) {
query.orderBy(cb.asc(cb.lower(root.get("description"))));
} else {
throw new UnsupportedOperationException("Unsupported sorting: " + sorting.name());
}
query.select(root);
return em.createQuery(query)
.setFirstResult(pageNumber * pageSize)
.setMaxResults(pageSize)
.getResultList();
}
getTotalCount selects count(distinct(*)),
private long getTotalCount(String descriptionFilter) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<Audi> root = query.from(Audi.class);
query.where(createSpecification(descriptionFilter).toPredicate(root, query, cb));
query.select(cb.countDistinct(root));
// getSingleResult can return null, if no rows fulfill the predicate
return Optional.ofNullable(em.createQuery(query).getSingleResult()).orElse(0L);
}
Both reuse the same predicate, which filters rows:
private Specification<Audi> createSpecification(String descriptionFilter) {
return Specification.where(
(root, query, criteriaBuilder) ->
criteriaBuilder.like(criteriaBuilder.lower(root.get("description")), "%" + descriptionFilter.toLowerCase() + "%")
);
}
Related
I have a repo with own Specification implementation with toPredicate method as main query construction and I try to add order by expression:
public Predicate toPredicate(#NotNull Root<Strategy> root,
#NotNull CriteriaQuery<?> query,
#NotNull CriteriaBuilder builder) {
Predicate predicate = builder.conjunction();
List<Expression<Boolean>> exps = predicate.getExpressions();
... adding different expressions to exps.add(...)
// I get an id for descending sort due to Postgres just increment it.
Order orderBy = builder.desc(root.get("id"));
Expression<?> expression = orderBy.getExpression();
// Problem here.
exps.add(expression);
return predicate;
}
Expression from orderBy.getExpression() is <?> generic but original expressions list expect <Boolean> type. How to connect them?
Specification is only intended for encoding where-clauses. If you want to order your result use a Sort instance as an additional parameter.
Sorting with pagination means sorting should be field of Pageable like this:
Pageable pagination = PageRequest.of(pageNumber, pageSize, Sort.by("id").descending());
I am having a bit of trouble with projecting and paging with mongooperations. I always get empty result. The query criteria working fine without aggregation. I tryed the same code without paging(skip&limit) and without sort but I still get the empty result.
My code:
public List<ProfileBasic> findAllActiveUsersByGenderAndAgeBetweenProjectedPage(Gender gender, int fromAge, int toAge, int pageSize, int page) {
Criteria criteria = Criteria.where("gender").is(gender).and("confirmed").is(true)
.and("dateOfBirth").lte(LocalDate.now().minusYears(fromAge))
.gte(LocalDate.now().minusYears(toAge));
MatchOperation match = Aggregation.match(criteria);
ProjectionOperation project = Aggregation.project()
.and("id").as("id")
.and("name").as("name")
.and("lastName").as("lastName")
.and("gender").as("gender")
.and("dateOfBirth").as("dateOfBirth")
.and("lastVisit").as("lastVisit");
SkipOperation skip = new SkipOperation(pageSize*(page-1));
LimitOperation limit = new LimitOperation(pageSize);
SortOperation sort = new SortOperation(new Sort(Sort.Direction.DESC, "lastVisit"));
Aggregation aggregate = Aggregation.newAggregation(project, match, skip, limit, sort);
return operations.aggregate(aggregate, User.class, ProfileBasic.class).getMappedResults();
}
I would appreciate any help.
When we do Aggregation in MongoDB data is processed in stages and the output of one stage is provided as input to the next stage.
In my code first stage was project and not match(query):
Aggregation aggregate = Aggregation.newAggregation(project, match, skip, limit, sort);
Thats why it didn't work.
I changed it to:
Aggregation aggregate = Aggregation.newAggregation(match, skip, limit, project, sort);
And now it works fine.
There're two numeric columns in database like actual and plan.
I can add third column like difference and write WHERE part of query filtering by this column.
But instead having additional column in database, I want to calculate it in Predicate.
Something like (qProduct.actualPrice - qProduct.planPrice).gt(20L)
Entity:
public class Product {
private String type;
private Long actualPrice;
private Long planPrice;
}
Predicate:
QProduct qProduct = QProduct.product;
BooleanExpression predicate = qProduct.type.eq("pizza")
.and((qProduct.actualPrice - qProduct.planPrice).gt(20L));
Page<Product> productPage = productRepository.findAll(predicate, pageable);
Thx
You can use the subtract method of NumberExpression class
QProduct qProduct = QProduct.product;
BooleanExpression predicate = qProduct.type.eq("pizza")
.and((qProduct.actualPrice.subtract(qProduct.planPrice)).gt(20L));
Page<Product> productPage = productRepository.findAll(predicate, pageable);
I am trying to implement pagination feature in Spring Data JPA.
I am referring this Blog
My Controller contains following code :
#RequestMapping(value="/organizationData", method = RequestMethod.GET)
public String list(Pageable pageable, Model model){
Page<Organization> members = this.OrganizationRepository.findAll(pageable);
model.addAttribute("members", members.getContent());
float nrOfPages = members.getTotalPages();
model.addAttribute("maxPages", nrOfPages);
return "members/list";
}
My DAO is following :
#Query(value="select m from Member m", countQuery="select count(m) from Member m")
Page<Organization> findMembers(Pageable pageable);
I am able to show first 20 records, how do I show next 20???
Is there any other pagination example that I can refer??
The constructors of Pageable are deprecated, use of() instead:
Pageable pageable = PageRequest.of(0, 20);
I've seen similar problem last week, but can't find it so I'll answer directly.
Your problem is that you specify the parameters too late. Pageable works the following way: you create Pageable object with certain properties. You can at least specify:
Page size,
Page number,
Sorting.
So let's assume that we have:
PageRequest p = new PageRequest(2, 20);
the above passed to the query will filter the results so only results from 21th to 40th will be returned.
You don't apply Pageable on result. You pass it with the query.
Edit:
Constructors of PageRequest are deprecated. Use Pageable pageable = PageRequest.of(2, 20);
Pageable object by default comes with size 20, page 0, and unsorted
So if you want the next page in front end the url can be sent with query params page, size,sort and these u can test it on postman.
You can use Page, List or Slice.
If you dont need the number of pages, and only need to know if the next page exists, use Slice, since it does not do the "count" query:
for (int i = 0; ; i++) {
Slice<Organization> pageOrganization = organizationRepository.find(PageRequest.of(0, 100));
List<Organization> organizationList = pageOrganization.getContent();
for (Organization org : organizationList) {
// code
}
if (!pageOrganization.hasNext()) {
break;
}
}
I'm using Eclipselink and have a tricky problem regarding JPA NamedQueries.
My database table contains a column which is from type VARCHAR and stores a comma separated list of keywords as one String.
How can I create a NamedQuery in JPA to search theese keywords?
I'd like to give a list of Strings as a parameter and as a result I'd like to have a list of objects which keyword list contain one of the Strings from the parameter list.
Maybe like the following:
List<String> keywordList = new ArrayList<String>();
keywordList.add("test");
keywordList.add("car");
List<Object> result = em.createNamedQuery("findObjectByKeywords", Object.class)
.setParameter("keywords", keywordList)
.getResultList();
Unfortunately I'm not such a big database/SQL expert. Maybe someone of you can help me?
I hope you understand my problem.
Edit:
I am developing on Weblogic 10.3.6, which means I am not able to use JPA 2.0 features.
Edit2:
I managed to activate JPA 2.0 in my Weblogic Server with the help of Oracle Enterprise Pack for Eclipse. Problem solved, I think.
VALID FOR JPA2.0
As Bhesh commented a simple JPQL won't make it. The resulting SQL has to contain a where clause similar to following:
where keywords like '%keyword1%' or keywords like '%keyword2%' or ... or keywords like '%keywordN%'
This means: We need a loop here!
You could try to build a JPQL by yourself like Bhesh suggested in his first comment, though as he also stated it is not a brilliant idea. But don't worry - JPA provides also a Criteria API which comes handy in such situations. So, although you're not going to have a named query, you can still make it with JPA this way:
public List<YourEntity> findAllByKeywords(List<String> keywords){
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<YourEntity> query = builder.createQuery(YourEntity.class);
Root<YourEntity> root = query.from(YourEntity.class);
List<Predicate> predicates = new LinkedList<>();
for (String keyword : keywords) {
predicates.add(builder.like(root.<String>get("keywords"), "%" + keyword + "%"));
}
return entityManager.createQuery(
query.select(root).where(
builder.or(
predicates.toArray(new Predicate[predicates.size()])
)
))
.getResultList();
}
or (always slightly better with Guava)
public List<YourEntity> findAllByKeywords(List<String> keywords){
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<YourEntity> query = builder.createQuery(YourEntity.class);
final Root<YourEntity> root = query.from(YourEntity.class);
return entityManager.createQuery(
query.select(root).where(
builder.or(
transform(keywords, toPredicateFunction(builder, root)).toArray(new Predicate[]{})
)
))
.getResultList();
}
private Function<String, Predicate> toPredicateFunction(final CriteriaBuilder builder, final Root<YourEntity> root) {
return new Function<String, Predicate>() {
#Override
public Predicate apply(String input) {
return builder.like(root.<String>get("keywords"), "%" + input + "%");
}
};
}