设为首页 加入收藏

TOP

使用 Java 注解自动化处理对应关系实现注释代码化(一)
2018-04-14 06:06:19 】 浏览:633
Tags:使用 Java 注解 自动化 处理 对应 关系 实现 注释 代码

概述

假设我们要从一个 ES 索引(相当于一张DB表)查询数据,ES表有 order_no, order_type, state 等字段, 而应用对象则有属性 orderNo, orderType, state等。这样,就会面临“将应用对象的属性与ES字段对应起来”的问题。

固然可以通过注释来说明,不过这样显得比较生硬。因为注释并不起实际作用,代码里还得写一套映射关系,就会存在注释与代码不一致的情况。 那么,是否可以将这种对应关系的注释用代码形式来解决呢? Java 注解可以解决这个问题。

实现

定义注解

首先定义注解类。注解类需要提供对应的ES字段名 name、类型 type 以及是否必传 required。

  • @Retention 指明注解在何时起作用,这里是在运行时。
  • @Target 指明注解应用于何种对象,这里应用于字段。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface EsField {

  /**
   * 对应的ES字段名
   */
  String name();

  /**
   * 对应的ES字段的值类型
   * @return
   */
  String type() default "";

  /**
   * 是否必传
   */
  boolean required() default false;

}

应用领域对象

接着,将注解应用到应用领域对象。为简洁,应用领域对象只有四个字段。

@Data
public class CustomerDomain implements DomainSearch {

  /** 店铺ID */
  @EsField(name="shop_id", required = true)
  private Long shopId;

  /** 订单编号 */
  @EsField(name="order_no")
  private String orderNo;

  /** 订单状态 */
  @EsField(name="state", type="list")
  private List<Integer> state;

  /** 订单类型 */
  @EsField(name="order_type", type="list")
  private List<Integer> orderType;

}

注解解析器

接着,需要提供注解解析器,将对应的映射关系转成ES查询对象的一部分。

  • 使用接口的默认方法来实现,是为了支持不同的业务类自动可以转化为ES查询串;
  • 注解解析器需要使用Java反射机制,来获取相应的字段,以及字段上的注解定义,然后根据字段的类型、值、注解定义来做相应处理;
  • 使用反射来处理字段时,由于字段一般是私有的,因此必须先设置为可访问的,处理完成后还原为不可访问;
  • EsField field = f.getAnnotation(EsField.class) 用来获取字段上的注解信息(name, type, required);Object value = f.get(customerDomain) 用来获取字段的值;字段的其他类型信息可以通过 Field 的方法拿到。
public interface DomainSearch {

  Log logger = LogFactory.getLog(DomainSearch.class);

  default String toEsQuery() {
    Object customerDomain = this;
    EsQuery esQuery = new EsQuery();
    Field[] fields = this.getClass().getDeclaredFields();
    for (Field f: fields) {
      try {
        if (Modifier.isStatic(f.getModifiers())) {
          continue;
        }

        f.setAccessible(true);

        Object value = f.get(customerDomain);

        if (f.getAnnotation(EsField.class) != null) {
          EsField field = f.getAnnotation(EsField.class);

          if (field.required() && value == null) {
            throw new RuntimeException("field '" + field + "' is required. value is null");
          }

          if (isNeedOmitted(value)) {
            f.setAccessible(false);
            continue;
          }

          if ((value instanceof List) && ((List)value).size() == 1) {
            // 针对 List 中单个值做优化查询
            esQuery = esQuery.addTermFilter(field.name(), ((List)value).get(0));
          }
          else {
            esQuery = esQuery.addTermFilter(field.name(), value);
          }
        }

        f.setAccessible(false);

      } catch (Exception ex) {
        logger.error("failed to build es query for field: " + f.getName(), ex);
        throw new RuntimeException(ex.getCause());
      }
    }
    return esQuery.toJsonString();
  }

  /**
   * 判断是否需要忽略该字段的查询
   * @param value 字段值
   * @return 是否要忽略
   */
  default boolean isNeedOmitted(Object value) {
    if (value == null) {
      return true;
    }

    // 空字符串搜索值忽略
    if ((value instanceof String) && StringUtils.isBlank(value.toString())) {
      return true;
    }

    // 空列表串忽略
    if ((value instanceof List) && ((List)value).isEmpty()) {
      return true;
    }
    return false;
  }

}

查询对象

ES查询对象将所有生成的查询条件转化为ES可以接受的查询字符串。

public class EsQuery {

  private static int DEFAULT_SIZE = 100;

  private final Map&l
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java 虚拟机16:Metaspace 下一篇Spring 中获取 request 的几种方..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目