Фильтр репозитория JPA с использованием предикатов Java 8 - PullRequest
0 голосов
/ 11 сентября 2018

У меня было требование в одном из моих тестов на собеседовании с использованием Spring Boot, где мне нужно было создать конечную точку, которая принимает множество необязательных параметров запроса, а затем возвращает список автомобилей на основе этих параметров, таких как модель автомобиля, номерной знак, двигательтип, производитель, водитель, компания, которой она была арендована и т. д. и т. д. И автомобиль, водитель и производитель - это отдельные объекты.

Я реализую эту функциональность в JPARepository с помощью одного запроса JPQL, реализующего СЛЕДУЮЩИЕ СОЕДИНЕНИЯ, и фильтрую гденапример, licensePlate = licensePlateParameter ИЛИ licensePlatParameter имеет значение null и т. д.

Решение работало, однако интервьюер сказал, что решение является масштабируемым и обслуживаемым.Я должен был реализовать это с помощью предикатов.Может кто-нибудь показать мне пример того, как я могу реализовать такую ​​функциональность, используя предикаты, которые легче поддерживать?Буду весьма признателен за некоторые примеры с кодом.

Я подумал, что был умен, поставив оба необязательных параметра и нашел записи в одном вызове, проверив, является ли параметр нулевым или нет.Другой вопрос, связанный с этим, который я имею в виду, это действительно хорошая практика, чтобы получить все записи из БД и затем отфильтровать их с помощью предикатов?Также, как мы фильтруем, когда задействовано несколько объектов / сущностей, предикаты могут быть созданы для одного типа.

@Query("SELECT d FROM Driver d LEFT JOIN d.car c WHERE (d.name = :name OR :name is null) "
            + "and (c.licensePlate = :licensePlate OR :licensePlate is null) "
            + "and (c.rating = :rating OR :rating is null) " and so on

    List<Driver> findByAttributes(@Param("name") String name, 
            @Param("licensePlate") String licensePlate,
            @Param("rating") Integer rating,
            and so on);

Ответы [ 3 ]

0 голосов
/ 11 сентября 2018

Вы можете использовать динамический запрос в Spring JPA следующим образом:

public List<Employee> findByCriteria(String employeeName,String employeeRole){
       return employeeDAO.findAll(new Specification<Employee>() {
           @Override
           public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
               List<Predicate> predicates = new ArrayList<>();
               if(employeeName!=null) {
                   predicates.add(criteriaBuilder.and(criteriaBuilder.like(root.get("employeeName"), "%"+employeeName+"%")));
               }
               if(employeeRole!=null){
                   predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("employeeRole"), employeeRole)));
               }
               return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
           }
       });
   }

Для этого вам нужно реализовать JpaSpecificationExecutor в вашем хранилище.

Вот подробное объяснение динамического запроса Spring JPA

0 голосов
/ 14 сентября 2018

Spring имеет обертку вокруг API критериев JPA (которая использует предикаты) и называется API спецификации.

Что вы можете сделать, когда пишете спецификации следующим образом, напишите спецификацию для каждого критерия:

public static Specification<Car> withLicensePlate(String licensePlate) {
    return (root, query, cb) -> licensePlate == null ? null : cb.equal(root.get("licensePlate"), licensePlate);
}

public static Specification<Car> withRating(String rating) {
    return (root, query, cb) -> rating == null ? null : cb.equal(root.get("rating"), rating);
}

public static Specification<Car> withName(String name) {
    return (root, query, cb) -> name == null ? null : cb.equal(root.get("name"), name);
}

также позволяет записать операцию соединения:

public static Specification<Car> withSeatType(String type) {
    return (root, query, cb) -> {
        return type == null ? null : cb.equal(root.join("interior", JoinType.LEFT).get("type"), type);
    };
}

Вы можете вернуть null в пределах критерия, что позволяет сделать эти спецификации "необязательными".После этого вы можете использовать Specifications.where() для объединения этих критериев:

 Specification<Car> spec = Specifications
      .where(withLicensePlate(licensePlate))
      .and(withRating(rating))
      .and(withName(name))
      .and(withSeatType(seatType));

Если вы пишете отдельные спецификации, как я делал в этом примере, вы можете использовать их при необходимости.В противном случае вам придется написать спецификации для конкретной операции, и интервьюер может и не найти такую ​​масштабируемую.

После написания спецификаций вы должны расширить свой репозиторий из интерфейса JpaSpecificationExecutor и использовать findAll(Specification) метод.

0 голосов
/ 11 сентября 2018

Вы можете использовать Criteria Api вместо JPQL.

Например, обратитесь к примеру 1

https://www.programcreek.com/java-api-examples/index.php?api=javax.persistence.criteria.Predicate

...