Spring JPA : Using Specification with Projection
You may probably know that using
I'll show you how you can easily bypass this problem using the JPA Criteria.
We begin by creating an extending global repository as follow :
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:
Basically, I'm defining the SELECT clause with the returned fields (cf.
😎Happy coding...
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 :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@NoRepositoryBean | |
public interface ExtendedRepository<T, ID extends Serializable> | |
extends JpaRepository<T, ID> { | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()
):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
😎Happy coding...
Comments
Post a Comment