springboot

Spring Data JPA 进阶:优雅实现 mybatis

2018-12-10  本文已影响45人  黄大海

这里介绍下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));
    

}
```

上一篇下一篇

猜你喜欢

热点阅读