Query By Example

QBE

介绍

  1. QBE 是一种用户友好的查询技术 , 有简单的界面
  2. 允许动态创建查询, 不需要编写包含字段名称
  3. 不需要使用特定数据源语言编写语句。

Usage

包含三部分:

  • Probe : 填充了字段的domain类实例 。
  • ExampleMatcher : 详细描述如何匹配特定字段, 可以在多个Example中交叉使用
  • Example : 一个Example包括probe 和exampleMatcher , 用来创建查询。

使用如下场景:

  • 使用静态或动态条件查询
  • 频繁重构domain对象 , 而不用担心破坏现有查询
  • 在特定数据源下使用

有如下限制:

  • 不支持属性嵌套或分组条件, 如 firstname = ?0 or (firstname = ?1 and lastname = ?2)
  • 仅支持字符串的 开始、结束、包含、正则匹配 , 其他类型的精确匹配。

示例:
创建一个domain类:

public class Person {

  @Id
  private String id;
  private String firstname;
  private String lastname;
  private Address address;

  // … getters and setters omitted
}

上例定义一个domain类,可以用他来创建Example 。 默认情况下,null字段将被忽略,String将使用特定数据源的默认值匹配。 可以使用工厂方法或ExampleMatcher创建。 Example是不可变对象immutable 。

Person person = new Person();                         
person.setFirstname("Dave");                          

Example example = Example.of(person);         
Create a new instance of the domain object.
Set the properties to query.
Create the Example.

Example使用repository执行。 这需要让你的repository接口继承 QueryByExampleExecutor , 如下:

public interface QueryByExampleExecutor {

   S findOne(Example example);

   Iterable findAll(Example example);

  // … more functionality omitted.
}

Example Matchers

Example不限制默认设置。 可以通过Example Matchers 来指定 string 匹配、 null处理、特定属性设置。

Person person = new Person();                          
person.setFirstname("Dave");                           

ExampleMatcher matcher = ExampleMatcher.matching()     
  .withIgnorePaths("lastname")                         
  .withIncludeNullValues()                             
  .withStringMatcherEnding();                          

Example example = Example.of(person, matcher); 

上例解释:

  1. 创建一个domain对象
  2. 设置属性值
  3. 创建一个ExampleMatcher ,不区分大小写匹配lastname 、包括null值、执行后缀匹配。
  4. 创建Example

默认情况下, ExampleMatcher 会匹配所有字段。

可以指定单个属性的行为(如 firstname and lastname 或者 内嵌属性"address.city") 。
可以调节匹配选项 和 大小写敏感:

ExampleMatcher matcher = ExampleMatcher.matching()
  .withMatcher("firstname", endsWith())
  .withMatcher("lastname", startsWith().ignoreCase());
}

可以在ExampleMatcher上设置默认匹配设置, 也可以为单个属性路径进行设置。 除非明确定义, 否则, ExampleMatcher上的设置都会被属性路径继承:

图片.png

Untyped Example

默认, Example 都是严格限制类型的 。
通过使用 UntypedExampleMatcher , 可以不限制类型。

class JustAnArbitraryClassWithMatchingFieldName {
  @Field("lastname") String value;
}

JustAnArbitraryClassWithMatchingFieldNames probe = new JustAnArbitraryClassWithMatchingFieldNames();
probe.value = "stark";

Example example = Example.of(probe, UntypedExampleMatcher.matching());

Query query = new Query(new Criteria().alike(example));
List result = template.find(query, Person.class);

其他实例

下例, 综合使用了排序, 分页 。
注意:

  1. 若Example的实体类中有基本类型,如int, 会初始化为0 ,这样查询语句中有以0作为输入进行比较, 目前使用的解决方案是 使用ExampleMatcher的withIgnorePaths 忽略该属性;
  2. 注意进行模糊查询的用法, .withMatcher("name", new GenericPropertyMatcher().contains()));
  3. 当前端上送一个串来模糊匹配多个字段时,如何实现 xxx like a or yyy like a, 目前Example 不支持。
    public RcDetailResponse queryRcControllDetail(RcDetailRequest req) {
        RcDetailResponse res = new RcDetailResponse();

        RcControllJournalDo mdo = new RcControllJournalDo();
        
        if (StringUtils.isNotBlank(req.getSearch())) {
            mdo.setName(req.getSearch());
            ///mdo.setIdno(req.getSearch());
        }
        
        Example example = Example.of(mdo, ExampleMatcher.matching()     
                  .withIgnorePaths("responsetm")
                  .withMatcher("name", new GenericPropertyMatcher().contains()));
        
        // 按交易时间降序排序 
        Sort sort = new Sort(Direction.DESC, "datetm");
        
        Pageable pageable = PageRequest.of(this.dataTable2PageRequest(req.getStart(), req.getLength()), req.getLength(), sort);
        
        Page result = null;
        
        try {
            result = this.rccJournalRepo.findAll(example, pageable);
        } catch (Exception e) {
            logger.error("查询风控明细数据出现异常!"  , e);
            
            res.setDraw(req.getDraw());
            res.setError("查询异常" + e.getMessage());
            res.setData(new ArrayList());
            res.setRecordsTotal(0); 
            res.setRecordsFiltered(0);
            return res;
        } 
        
        
        
        if (result == null ||  result.getContent() == null || result.getContent().size() == 0) {
            logger.debug("查询风控明细数据为空 !");
            
            res.setError("查询结果为空");
            res.setDraw(req.getDraw());
            res.setData(new ArrayList());
            res.setRecordsTotal(0); 
            res.setRecordsFiltered(0);
            return res;
        }
        
        logger.debug("查询风控明细数据结束,组织返回结果 !");
        
        res.setDraw(req.getDraw());
        res.setRecordsTotal((int) result.getTotalElements()); 
        res.setRecordsFiltered(res.getRecordsTotal());
        
        ArrayList data = new ArrayList();
        
        DateFormat df = new SimpleDateFormat(DATE_FORMAT14);
        
        for (RcControllJournalDo xdo : result.getContent()) {
            String dtm = df.format(xdo.getDatetm());
            data.add(new RcDetailDataDo(xdo.getName(), xdo.getIdno(), xdo.getPhone(), 
                    xdo.getSeqno(), dtm, this.translateStatus(xdo.getStatus(), xdo.getTranStatus()),
                    xdo.getResinfo(), xdo.getAmt()));
        }
        
        res.setData(data);
        
        return res;
    }
    
    /**
     * 转换datatable 开始条数到 PageRequest的页数
     * @param start
     * @param size
     * @return
     */
    private int dataTable2PageRequest(int start, int size) {
        return (start + 1)/size;
    }

你可能感兴趣的:(Query By Example)