Spring JPA : Using Specification with Projection

You may probably know that using Specification with Projection is not possible at this moment.

I'll show you how you can easily bypass this problem using the JPA Criteria.

1. Create an extended repository


We begin by creating an extending global repository as follow :

@NoRepositoryBean
public interface ExtendedRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
}

public class ExtendedRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
private static final Logger LOG = Logger.getLogger(ExtendedRepositoryImpl.class.getName());
private EntityManager entityManager;
public ExtendedRepositoryImpl(JpaEntityInformation<T, ?> entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
}


You can find more information here.

2. Add our custom method

In order to limit the fields returned for each item, the consumer of my API must provide the required fields this way: /orders?fields=productId,name,quantity. (Best Practices)

Basically, I'm defining the SELECT clause with the returned fields (cf. getSelections()):

public List<Tuple> findAllWithPagination(Specification<T> specs,
Pageable pageable,
List<String> fields) {
Assert.notNull(pageable, "Pageable must be not null!");
Assert.notEmpty(fields, "Fields must not be empty!");
// Create query
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = builder.createTupleQuery();
// Define FROM clause
Root<T> root = applySpecToCriteria(query, builder, specs);
// Define selecting expression
List<Selection<?>> selections = getSelections(fields, root);
query.multiselect(selections);
//Define ORDER BY clause
applySorting(builder, query, root, pageable);
return getPageableResultList(query, pageable);
}
private <R> Root<T> applySpecToCriteria(CriteriaQuery<R> query,
CriteriaBuilder builder,
Specification<T> specs) {
Assert.notNull(query, "CriteriaQuery must not be null!");
Root<T> root = query.from(getDomainClass());
if (specs == null) {
return root;
}
Predicate predicate = specs.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
}
return root;
}
private List<Selection<?>> getSelections(List<String> fields,
Root<T> root) {
List<Selection<?>> selections = new ArrayList<>();
for (String field : fields) {
selections.add(root.get(field).alias(field));
}
return selections;
}
private <R> void applySorting(CriteriaBuilder builder,
CriteriaQuery<R> query,
Root<T> root,
Pageable pageable) {
Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();
if (sort.isSorted()) {
query.orderBy(toOrders(sort, root, builder));
}
}
private <R> List<R> getPageableResultList(CriteriaQuery<R> query,
Pageable pageable) {
TypedQuery<R> typedQuery = entityManager.createQuery(query);
// Apply pagination
if (pageable.isPaged()) {
typedQuery.setFirstResult((int) pageable.getOffset());
typedQuery.setMaxResults(pageable.getPageSize());
}
return typedQuery.getResultList();
}
view raw gistfile1.txt hosted with ❤ by GitHub


😎Happy coding...


Comments

Popular posts from this blog

Chip input using Reactive Form