Запрос критериев Spring-Data-JPA

задняя часть Spring Безопасность API

Хотя Spring Data JPA значительно упрощает разработку уровня сохраняемости, в реальной разработке во многих местах требуются расширенные динамические запросы.

Criteria API

Критериальные запросы основаны на концепции метамодели, которая определяется для управляемых сущностей определенной единицы сохраняемости, которые могут быть классами сущностей, встроенными классами или сопоставленными родительскими классами.

Интерфейс CriteriaQuery: представляет собой конкретный объект запроса верхнего уровня, который содержит различные части запроса, такие как: выбор, из, где, группировка, упорядочивание и т. д. Примечание. Объект CriteriaQuery работает только с типом сущности или критериями встроенного типа. запрос

Корневой интерфейс: представляет корневой объект запроса «Критерии». Корень запроса «Критерии» определяет тип объекта и может получить желаемые результаты для будущей навигации. Он аналогичен предложению FROM в запросе SQL.

​ 1: экземпляр Root является типизированным и определяет типы, которые могут появляться в предложении FROM запроса.

​ 2: Корневой экземпляр запроса можно получить, передав тип сущности методу AbstractQuery.from.

​ 3: Критериальный запрос, который может иметь несколько корней запроса.

4: AbstractQuery — это родительский класс интерфейса CriteriaQuery, который предоставляет метод для получения корня запроса. Интерфейс CriteriaBuilder: объект-конструктор, используемый для построения CritiaQuery Predicate: простой или сложный тип предиката, фактически эквивалентный условию или комбинации условий.

Запрос является типобезопасным для объекта Java, если компилятор может выполнять проверку синтаксической правильности запроса. В версии 2.0 Java™ Persistence API (JPA) представлен Criteria API, который впервые обеспечивает типобезопасные запросы к приложениям Java и предоставляет механизм для динамического создания запросов во время выполнения.

метамодель JPA

В JPA стандартные запросы основаны на концепции метамодели. Метамодель определяется для управляемых сущностей конкретной единицы сохраняемости.Эти сущности могут быть классами сущностей, встроенными классами или сопоставленными родительскими классами.Управляемые сущности предоставляются Класс метаинформации является классом метамодели.

Самым большим преимуществом использования классов метамодели является то, что они могут получать доступ к постоянным свойствам сущностей во время компиляции в силу их создания, что делает запросы критериев более безопасными для типов.

следующее,ItemМетамодель, соответствующая классу сущностейItem_

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Item.class)
public abstract class Item_ {
   public static volatile SingularAttribute<Item, Integer> itemId;
   public static volatile SingularAttribute<Item, String> itemName;
   public static volatile SingularAttribute<Item, Integer> itemStock;
   public static volatile SingularAttribute<Item, Integer> itemPrice;
}

Такую метамодель не нужно создавать вручную, добавьте плагин к Maven, и класс, аннотированный @Entity, автоматически сгенерирует соответствующую метамодель после компиляции.

<!--hibernate JPA 自动生成元模型-->
<!-- 相关依赖 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>5.2.10.Final</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.1.0.Final</version>
        </dependency>
<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
       <configuration>
           <source>1.8</source>
             <target>1.8</target>
              <compilerArguments>
                 <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
              </compilerArguments>
         </configuration>
 </plugin>

Существует более одного метода, см. подробности:Chapter 2. Usage

Используйте критерии для запроса простой демонстрации

@Service
public class ItemServiceImpl implements ItemService {

    @Resource
    private EntityManager entityManager;

    @Override
    public List<Item> findByConditions(String name, Integer price, Integer stock) {
          //创建CriteriaBuilder安全查询工厂
        //CriteriaBuilder是一个工厂对象,安全查询的开始.用于构建JPA安全查询.
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        //创建CriteriaQuery安全查询主语句
        //CriteriaQuery对象必须在实体类型或嵌入式类型上的Criteria 查询上起作用。
        CriteriaQuery<Item> query = criteriaBuilder.createQuery(Item.class);
        //Root 定义查询的From子句中能出现的类型
        Root<Item> itemRoot = query.from(Item.class);
          //Predicate 过滤条件 构建where字句可能的各种条件
          //这里用List存放多种查询条件,实现动态查询
        List<Predicate> predicatesList = new ArrayList<>();
          //name模糊查询 ,like语句
        if (name != null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.like(
                                    itemRoot.get(Item_.itemName), "%" + name + "%")));
        }
         // itemPrice 小于等于 <= 语句
        if (price != null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.le(
                                    itemRoot.get(Item_.itemPrice), price)));
        }
        //itemStock 大于等于 >= 语句
        if (stock != null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.ge(
                                    itemRoot.get(Item_.itemStock), stock)));
        }
          //where()拼接查询条件
        query.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
        TypedQuery<Item> typedQuery = entityManager.createQuery(query);
        List<Item> resultList = typedQuery.getResultList();
        return resultList;
    }
}

Операторы, соответствующие каждому методу в критерииBuilder

equle : filed = value

gt / greaterThan : filed > value

lt / lessThan : filed < value

ge / greaterThanOrEqualTo : filed >= value

le / lessThanOrEqualTo: filed <= value

notEqule : filed != value

like : filed like value

notLike : filed not like value

Если это будет написано в каждом месте динамического запроса, это будет слишком хлопотно.

На самом деле, при использовании Spring Data JPA, пока наш интерфейс уровня репо наследует интерфейс JpaSpecificationExecutor, мы можем использовать спецификацию для динамического запроса.Давайте сначала посмотрим на интерфейс JpaSpecificationExecutor:

public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> var1);

    List<T> findAll(Specification<T> var1);

    Page<T> findAll(Specification<T> var1, Pageable var2);

    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);
}

Здесь очень важный интерфейсSpecification

public interface Specification<T> {
    Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}

Этот интерфейс имеет только один метод, который возвращает структуру данных динамического запроса, который используется для построения SQL различных динамических запросов.

Пример интерфейса спецификации

public Page<Item> findByConditions(String name, Integer price, Integer stock, Pageable page) {
     Page<Item> page = itemRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicatesList = new ArrayList<>();
            //name模糊查询 ,like语句
            if (name != null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.like(
                                        root.get(Item_.itemName), "%" + name + "%")));
            }
            // itemPrice 小于等于 <= 语句
            if (price != null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.le(
                                        root.get(Item_.itemPrice), price)));
            }
            //itemStock 大于等于 >= 语句
            if (stock != null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.ge(
                                        root.get(Item_.itemStock), stock)));
            }
            return criteriaBuilder.and(
                    predicatesList.toArray(new Predicate[predicatesList.size()]));
        }, page);
    return page;
}

здесь, потому чтоfindAll(Specification<T> var1, Pageable var2)параметры в методеSpecification<T>является анонимным внутренним классом

Затем вы можете напрямую использовать лямбда-выражения для непосредственного упрощения кода.

Таким образом, это намного проще, чем использовать CriteriaBuilder для безопасного запроса фабрики.

перечислить:

Page<Item> itemPageList = findByConditions("车", 300, null, new PageRequest(1, 10));

Использование JPASpecification<T>Интерфейсы и метамодели реализуют динамические запросы.

На самом деле, везде, где нужен динамический запрос, нужно написать аналогичный запрос.findByConditionsСпособ тоже очень хлопотный, конечно, чем проще, тем лучше.

В следующей статье речь пойдет о JPA.Specificationудобнее в использовании.
Оригинальная ссылка:Запрос критериев Spring-Data-JPA | Хуо Яо