zoukankan      html  css  js  c++  java
  • Spring Data Jpa

    Introduction

    在基本的数据查询实例中,可以通过实现CrudRepository接口来实现针对一个的查询或多个字段的组合查询,但这只是对于条件比较简单的情况下,如果条件比较复杂,那么一个方法的名字就会显的很长,那么就可以换一种方式来实现数据查询,比如下面即将提到的Criteria API, Specification, Query dsl.

    Criteria API

    CriteriaQuery

    一个典型的查询代码如下:

    LocalDate today = new LocalDate();
    
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
    Root<Customer> root = query.from(Customer.class);
    
    Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
    Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2); 
    query.where(builder.and(hasBirthday, isLongTermCustomer));
    em.createQuery(query.select(root)).getResultList();
    

    主要步骤如下:

    1. 创建查询构造器CriteriaBuilder
    2. 实例化一个条件查询
    3. 创建一个查询的根元素
    4. 创建一个或多个查询的条件预测
    5. entityManager上执行查询
    CriteriaUpdate/CriteriaDelete

    CriteriaQuery是jpa 2.0引入的接口,而在jpa 2.1中还引入了CriteriaUpdateCriteriaDelete接口来实现对数据的修改和删除操作。 CriteriaUpdate的实现和CriteriaQuery类似,其实现如下:

    CriteriaBuilder builder = entityManager
        .getCriteriaBuilder();
    
    CriteriaUpdate<T> update = builder
        .createCriteriaUpdate(postModerateClass);
    
    Root<T> root = update.from(postModerateClass);
    
    Expression<Boolean> filterPredicate = builder
    .like(
        builder.lower(root.get("message")), 
        "%spam%"
    );
    
    if(Post.class.isAssignableFrom(postModerateClass)) {
        filterPredicate = builder.or(
            filterPredicate, builder
            .like(
                builder.lower(root.get("title")), 
                "%spam%"
            )
        );
    }
    
    update
    .set(root.get("status"), PostStatus.SPAM)
    .set(root.get("updatedOn"), new Date())
    .where(filterPredicate);
    
    return entityManager
    .createQuery(update)
    .executeUpdate()
    

    当然如果觉的写那么多模板代码比较麻烦,也可以直接通过EntityManagercreateQuery接口手写sql, 也是可以的。也有其他的方法,如下面的Specification接口。

    Specification接口

    Specification接口实现了可重用的预测(Predicate), 一个Specification接口就是一个查询条件,可以通过创建多个Specification接口实现复杂的条件查询,其接口如下:

    public interface Specification<T> {
      Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
    }
    

    Root, CriteriaQuery, CriteriaBuilderCriteria中的一样,下面是一个示例:

    public CustomerSpecifications {
    
      public static Specification<Customer> customerHasBirthday() {
        return new Specification<Customer> {
          public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
            return cb.equal(root.get(Customer_.birthday), today);
          }
        };
      }
    
      public static Specification<Customer> isLongTermCustomer() {
        return new Specification<Customer> {
          public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
            return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
          }
        };
      }
    }
    

    为了让repository可以执行这个查询,需要repository接口实现JpaSpecificationExecutor接口。查询实例如下:

    customerRepository.findAll(hasBirthday());
    

    或者实现组合查询(jpa中提供了where, and, or 等帮助方法来简化操作):

    customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));
    

    Querydsl

    开源项目Querydsl也提供了简化模板代码的实现, 你需要pom.xml中添加querydsl的包,然后添加插件以实现在每个源码包下自动创建查询类比如QCustomer

    <plugin>
      <groupId>com.mysema.maven</groupId>
      <artifactId>maven-apt-plugin</artifactId>
      <version>1.0</version>
      <executions>
        <execution>
          <phase>generate-sources</phase>
          <goals>
            <goal>process</goal>
          </goals>
          <configuration>
            <outputDirectory>target/generated-sources</outputDirectory>
            <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
          </configuration>
        </execution>
      </executions>
    </plugin>
    

    查询代码如下:

    BooleanExpression customerHasBirthday = customer.birthday.eq(today);
    BooleanExpression isLongTermCustomer = customer.createdAt.lt(today.minusYears(2));
    customerRepository.findAll(customerHasBirthday.and(isLongTermCustomer));
    

    其中BooleanExpression和Specification类似,当然,repository需要实现相应的接口

    public interface CustomerRepository extends JpaRepository<Customer>, QueryDslPredicateExecutor {
    }
    

    https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/ https://vladmihalcea.com/jpa-criteria-api-bulk-update-delete/

  • 相关阅读:
    STM32:SPI&w25qxx的配置与代码
    STM32:USART的原理与配置
    C的抽象数据类型:二叉树
    DSP:TMS320C66x 系列SPI NOR自启动
    C的抽象数据类型:链表、队列
    STM32:GPIO口的使用
    STM32:时钟树
    STM32:预备知识
    makefile:简单小结
    ubuntu:tar、apt、vim、gcc的配置和简单使用
  • 原文地址:https://www.cnblogs.com/helloz/p/9539340.html
Copyright © 2011-2022 走看看