简体   繁体   中英

Spring JPA - How to return distinct results based on multiple columns

I have a table that looks kinda like this

Entity Name Year Person Name
School 1 2021 Person 1
School 1 2021 Person 2
School 1 2020 Person 1
School 2 2021 Person 1

I want to be able to use Spring JPA to return only one result per Entity Name. For example, the results would be School 1, School 2. I don't care which School 1 row is retrieved, I just need to only retrieve one. I need to be able to use a Specification, return Pagination results, and sort the results.

Here is what I have tried so far

Repository

@Repository
public interface Repository extends JpaRepository<Model, UUID>,
        JpaSpecificationExecutor<Model>{
}

Service

repository.findAll(Specification
                .searchByDtoSpec(searchDto), page)
                .map(this::mapToDto);

Specification

    public static Specification<Model> searchByDtoSpec(
            final SearchDto searchDto) {
        return (root, query, cb) -> {
            final List<Predicate> predicates = new ArrayList<>();

            query.multiselect(root.get("entityName"),root.get("personName")).distinct(true);

            return cb.and(predicates.toArray(Predicate[]::new));
        };
    }

To answer my own question, I was not able to find a way without using CriteriaBuilder and CriteriaQuery. I had to combine a CriteriaQuery multiselect and a CriteriaQuery distinct Here is a snippet of my code to help anyone else in my situation:

public Page<Model> findAllByFilters(final Pageable page,
            final SearchDto searchDto) {
        final CriteriaBuilder cb = em.getCriteriaBuilder();

        final CriteriaQuery<Model> cq = cb.createQuery(Model.class);

        final Root<Model> root = cq.from(Model.class);

        final List<Predicate> predicates = getCustomPredicates(root, cq, cb, searchDto, 
            currentDate, false);
        cq.where(predicates.toArray(Predicate[]::new));
        cq.multiselect(root.get("entityName"), root.get("year"), root.get("personName");
        cq.distinct(true);

        final TypedQuery<Model> tq = em.createQuery(cq);

        // Code for Pagination
        ...

        final List<Model> list = tq.getResultList();
        return new PageImpl<Model>(list, page, count);
    }

In your Repository interface add a method like this

Model findFirstByOrderByPersonNameAsc();

You can order it however you want. You can also return an optional Optional<Model> if you would like.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM