Skip to content

Commit

Permalink
fix(citrus-simulator): include jpa criteria in all scenario execution…
Browse files Browse the repository at this point in the history
… queries
  • Loading branch information
bbortt committed Jan 26, 2025
1 parent c19974d commit 50af760
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 212 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,23 @@

package org.citrusframework.simulator.service;

import static java.util.Objects.nonNull;
import static org.springframework.data.domain.Pageable.unpaged;

import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Order;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.SingularAttribute;
import java.util.ArrayList;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.nonNull;
import static org.springframework.data.domain.Pageable.unpaged;

final class CriteriaQueryUtils {

private CriteriaQueryUtils() {
Expand All @@ -57,32 +59,49 @@ private CriteriaQueryUtils() {
*
* @see <a href="https://github.com/citrusframework/citrus-simulator/issues/255">problem with in-memory pagination when having high volume of data</a>
*/
static <R> TypedQuery<Long> newSelectIdBySpecificationQuery(Class<R> entityClass, SingularAttribute<R, Long> idAttribute, Specification<R> specification, Pageable page, EntityManager entityManager) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
static <E> List<Long> selectAllIds(Class<E> entityClass, SingularAttribute<E, Long> idAttribute, Specification<E> specification, Pageable page, EntityManager entityManager) {
var criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
Root<R> root = criteriaQuery.from(entityClass);
Root<E> root = criteriaQuery.from(entityClass);

criteriaQuery = selectIdField(idAttribute, criteriaQuery, root);
criteriaQuery = selectIdField(idAttribute, root, criteriaQuery);

criteriaQuery = whereSpecificationApplies(specification, root, criteriaQuery, criteriaBuilder);

criteriaQuery = orderByPageSort(page, root, criteriaBuilder, criteriaQuery);
criteriaQuery = orderBy(page.getSort(), root, criteriaQuery, criteriaBuilder);

TypedQuery<Long> query = entityManager.createQuery(criteriaQuery);

if (!unpaged().equals(page)) {
query = selectPage(page, query);
}

return query;
return query.getResultList();
}

/**
* Find all entities matching the {@link Specification}.
*/
static <E> List<E> selectAll(Class<E> entityClass, Specification<E> specification, Pageable page, EntityManager entityManager) {
var criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<E> criteriaQuery = criteriaBuilder.createQuery(entityClass);
Root<E> root = criteriaQuery.from(entityClass);

criteriaQuery = whereSpecificationApplies(specification, root, criteriaQuery, criteriaBuilder);

criteriaQuery = orderBy(page.getSort(), root, criteriaQuery, criteriaBuilder);

TypedQuery<E> query = entityManager.createQuery(criteriaQuery);

return query.getResultList();
}

/**
* Restrict the query to the ID of the entity.
*
* @return the modified {@link CriteriaQuery<Long>}.
*/
private static <R> CriteriaQuery<Long> selectIdField(SingularAttribute<R, Long> idAttribute, CriteriaQuery<Long> criteriaQuery, Root<R> root) {
private static <R> CriteriaQuery<Long> selectIdField(SingularAttribute<R, Long> idAttribute, Root<R> root, CriteriaQuery<Long> criteriaQuery) {
return criteriaQuery.select(root.get(idAttribute));
}

Expand All @@ -91,8 +110,8 @@ private static <R> CriteriaQuery<Long> selectIdField(SingularAttribute<R, Long>
*
* @return the modified {@link CriteriaQuery<Long>}.
*/
private static <R> CriteriaQuery<Long> whereSpecificationApplies(Specification<R> specification, Root<R> root, CriteriaQuery<Long> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Predicate predicate = specification.toPredicate(root, criteriaQuery, criteriaBuilder);
private static <E, R> CriteriaQuery<R> whereSpecificationApplies(Specification<E> specification, Root<E> root, CriteriaQuery<R> criteriaQuery, CriteriaBuilder criteriaBuilder) {
var predicate = specification.toPredicate(root, criteriaQuery, criteriaBuilder);
if (nonNull(predicate)) {
return criteriaQuery.where(predicate);
}
Expand All @@ -104,15 +123,16 @@ private static <R> CriteriaQuery<Long> whereSpecificationApplies(Specification<R
*
* @return the modified {@link CriteriaQuery<Long>}.
*/
private static <R> CriteriaQuery<Long> orderByPageSort(Pageable page, Root<R> root, CriteriaBuilder criteriaBuilder, CriteriaQuery<Long> criteriaQuery) {
private static <E,R> CriteriaQuery<E> orderBy(Sort sort, Root<R> root, CriteriaQuery<E> criteriaQuery, CriteriaBuilder criteriaBuilder) {
var orders = new ArrayList<Order>();
for (var sortOrder : page.getSort()) {
for (var sortOrder : sort) {
var path = root.get(sortOrder.getProperty());
orders.add(sortOrder.isAscending() ? criteriaBuilder.asc(path) : criteriaBuilder.desc(path));
}
if (!orders.isEmpty()) {
return criteriaQuery.orderBy(orders);
}

return criteriaQuery;
}

Expand All @@ -121,7 +141,7 @@ private static <R> CriteriaQuery<Long> orderByPageSort(Pageable page, Root<R> ro
*
* @return The same {@link TypedQuery<Long>}.
*/
private static TypedQuery<Long> selectPage(Pageable page, TypedQuery<Long> query) {
private static <E> TypedQuery<E> selectPage(Pageable page, TypedQuery<E> query) {
// Calculate the first result index based on the page number and page size
var pageSize = page.getPageSize();
var firstResult = page.getPageNumber() * pageSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@

import java.util.List;

import static org.citrusframework.simulator.service.CriteriaQueryUtils.newSelectIdBySpecificationQuery;
import static org.springframework.data.domain.Pageable.unpaged;
import static org.citrusframework.simulator.service.CriteriaQueryUtils.selectAllIds;

/**
* Service for executing complex queries for {@link Message} entities in the database.
Expand Down Expand Up @@ -67,16 +66,15 @@ public Page<Message> findByCriteria(MessageCriteria criteria, Pageable page) {
logger.debug("find by criteria : {}, page: {}", criteria, page);

var specification = createSpecification(criteria);
var messageIds = newSelectIdBySpecificationQuery(
var messageIds = selectAllIds(
Message.class,
Message_.messageId,
specification,
page,
entityManager
)
.getResultList();
);

var messages = messageRepository.findAllWhereMessageIdIn(messageIds, unpaged(page.getSort()));
var messages = messageRepository.findAllWhereMessageIdIn(messageIds, page);
return new PageImpl<>(messages.getContent(), page, messageRepository.count(specification));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.citrusframework.simulator.service;

import jakarta.annotation.Nullable;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Root;
Expand All @@ -32,6 +33,8 @@
import java.util.Set;
import java.util.function.Function;

import static java.util.Objects.isNull;

/**
* Base service for constructing and executing complex queries.
*
Expand Down Expand Up @@ -145,36 +148,35 @@ protected <X extends Comparable<? super X>> Specification<ENTITY> buildRangeSpec
* @param metaclassFunction lambda, which based on a Root&lt;ENTITY&gt; returns Expression - basicaly picks a column
* @return a Specification
*/
protected <X extends Comparable<? super X>> Specification<ENTITY> buildSpecification(RangeFilter<X> filter,
Function<Root<ENTITY>, Expression<X>> metaclassFunction) {
protected <X extends Comparable<? super X>> Specification<ENTITY> buildSpecification(
@Nullable RangeFilter<X> filter,
Function<Root<ENTITY>, Expression<X>> metaclassFunction
) {
Specification<ENTITY> result = Specification.where(null);
if (isNull(filter)) {
return result;
}

if (filter.getEquals() != null) {
return equalsSpecification(metaclassFunction, filter.getEquals());
} else if (filter.getIn() != null) {
return valueIn(metaclassFunction, filter.getIn());
}

Specification<ENTITY> result = Specification.where(null);
if (filter.getSpecified() != null) {
} else if (filter.getSpecified() != null) {
result = result.and(byFieldSpecified(metaclassFunction, filter.getSpecified()));
}
if (filter.getNotEquals() != null) {
} else if (filter.getNotEquals() != null) {
result = result.and(notEqualsSpecification(metaclassFunction, filter.getNotEquals()));
}
if (filter.getNotIn() != null) {
} else if (filter.getNotIn() != null) {
result = result.and(valueNotIn(metaclassFunction, filter.getNotIn()));
}
if (filter.getGreaterThan() != null) {
} else if (filter.getGreaterThan() != null) {
result = result.and(greaterThan(metaclassFunction, filter.getGreaterThan()));
}
if (filter.getGreaterThanOrEqual() != null) {
} else if (filter.getGreaterThanOrEqual() != null) {
result = result.and(greaterThanOrEqualTo(metaclassFunction, filter.getGreaterThanOrEqual()));
}
if (filter.getLessThan() != null) {
} else if (filter.getLessThan() != null) {
result = result.and(lessThan(metaclassFunction, filter.getLessThan()));
}
if (filter.getLessThanOrEqual() != null) {
} else if (filter.getLessThanOrEqual() != null) {
result = result.and(lessThanOrEqualTo(metaclassFunction, filter.getLessThanOrEqual()));
}

return result;
}

Expand Down Expand Up @@ -533,7 +535,7 @@ protected <X extends Comparable<? super X>> Specification<ENTITY> lessThan(Funct
* @param txt a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
protected String wrapLikeQuery(String txt) {
protected static String wrapLikeQuery(String txt) {
return "%" + txt.toUpperCase() + '%';
}

Expand Down
Loading

0 comments on commit 50af760

Please sign in to comment.