美文网首页springboot
Spring Data JPA 进阶:优雅实现 mybatis

Spring Data JPA 进阶:优雅实现 mybatis

作者: 黄大海 | 来源:发表于2018-12-10 18:47 被阅读45次
    • JPA和Mybatis相比还是比较复杂的,Spring Data JPA极大的简化了JPA的处理。但是在处理复杂查询方面比较繁琐。
    • 用惯Mybais的人转到Spring Data JPA可能最大的抱怨就是处理IF/ELSE条件。只有条件非空时才加入条件。

    这里介绍下mybatis style 的查询的几种方法

    1. QBE(QueryByExample)

      • 这个可以处理等值比较和like查询,所有非空字段都会加入条件
      Book example = new Book();
      example.setColor("Black")
      example.setSize("Big")
      
      bookRepository.findAll(Example.of(example));
      
      //select * from BOOK where color = 'Black' and size = 'Big'
      
    2. QBE不能处理NOT,IN, BETWEEN,<, > 这些条件。官方提供的方案叫Specification

      • 大概是这个感觉:
      public class BookSpecifications {
          public static Specification<Book> sizeNot(final String size){
              return new Specification<Book>(){
                  @Override
                  public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                      return cb.not(root.<String>get("size"), size);
                  }
              };
          }
          
          public static Specification<Book> categoryIn(final List<String> categories){
              return new Specification<Book>(){
                  @Override
                  public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                      return root.<String>get("category")).in(categories);
                  }
              };
          }
      }   
      
      ... 
      
      Specification spec = Specifications
          .where(BookSpecifications.sizeNot("small"))
          .and(BookSpecifications.categoryIn(...));
      bookRepository.findAll(spec);
      
      • JDK1.8后,我们可以用lambda来简化
      Specification spec = Specifications
          .<Book>where((r,q,c) -> c.not(r.<String>get("size"), size))
          .and((r,q,c) -> r.<String>get("category")).in(categories));
      bookRepository.findAll(spec);
      
      • 这里还没有空值判断。Specification动态拼装很不方便,因为第一个用的是where然后才是and,需要额外的处理。这里可以用CriteriaBuilder#conjunction()来处理,该条件输出一个 1=1 条件
      Specification spec = Specifications
          .<Book>where((r,q,c) -> StringUtils.isEmpty() ? c.not(r.<String>get("size"), size) : c.conjunction())
          .and((r,q,c) -> categories.isEmpty() ? c.conjunction() : r.<String>get("category")).in(categories));
      bookRepository.findAll(spec);
      
      //当两个条件都为空时:select * from BOOK where 1=1 and 1=1;
      
    3. 结合QBE和Specification

      Book example = new Book();
      example.setColor("Black")
      example.setSize("Big")
      
      Specification spec = Specifications
          .<Book>where(new ExampleSpecification<>(Example.of(example)))
          .and((r,q,c) -> StringUtils.isEmpty() ? c.not(r.<String>get("size"), size) : c.conjunction())
          .and((r,q,c) -> categories.isEmpty() ? c.conjunction() : r.<String>get("category")).in(categories));
      bookRepository.findAll(spec);
      
      • 这里的ExampleSpecification是SimpleJpaRepository. ExampleSpecification,该类是私有的,必须拷贝一份改为public使用。或者可以拷贝其内部代码:
      Specifications.where((r,q,c) -> QueryByExamplePredicateBuilder.getPredicate(r, c, Example.of(example)))
      
      • 还可以利用default方法:
      public interface BookRepository extends JpaRepository<Book, Long>, JpaSpecificationExecutor<Book>{
          public default findByQbeAndSpec(Book example, String sizeNot, List<String> categories){
              Specification spec = Specifications
                  .<Book>where(new ExampleSpecification<>(Example.of(example)))
                  .and((r,q,c) -> StringUtils.isEmpty() ? c.not(r.<String>get("size"), size) : c.conjunction())
                  .and((r,q,c) -> categories.isEmpty() ? c.conjunction() : r.<String>get("category")).in(categories));
              return findAll(spec);
          }
      }
      
    4. 当然也有大神站出来替我们实现了一个工具包,比原版的好用:jpa-spec

      public Page<Person> findAll(SearchRequest request) {
      Specification<Person> specification = Specifications.<Person>and()
              .eq(StringUtils.isNotBlank(request.getName()), "name", request.getName())
              .gt(Objects.nonNull(request.getAge()), "age", 18)
              .between("birthday", new Date(), new Date())
              .like("nickName", "%og%", "%me")
              .build();
      
      return personRepository.findAll(specification, new PageRequest(0, 15));
      

    }
    ```

    相关文章

      网友评论

        本文标题:Spring Data JPA 进阶:优雅实现 mybatis

        本文链接:https://www.haomeiwen.com/subject/mppdhqtx.html