您的位置 首页 java

使用 Java 注解自动化处理对应关系实现注释代码化

概述

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

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

实现

定义注解

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

  • @Retention 指明注解在何时起作用,这里是在运行时。
  • @Target 指明注解应用于何种对象,这里应用于字段。

应用领域对象

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

@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<String, Object> termFilter;

private final Map<String, Range> rangeFilter;

private final Map<String, Match> matchFilter;

private int size;

private String orderBy = null ;

private String order = null ;

// query 查询语法, 是否需要 filtered, filter 这两层

// 5.x 版本不再需要这两层

private bool ean isNeedFilterLayer = true ;

private Integer from;

private final Map<String, Object> mustNotTermFilter;

private final Map<String,Object> shouldTermFilter;

private Integer shouldMatchMinimum;

private List<String> includes;

private List<String> excludes;

public EsQuery() {

this .termFilter = new HashMap<>();

this .rangeFilter = new HashMap();

this .matchFilter = new HashMap();

this .mustNotTermFilter = new HashMap<>();

this .shouldTermFilter = new HashedMap();

this .size = DEFAULT_SIZE;

this .includes = new ArrayList<>();

this .excludes = new ArrayList<>();

}

public EsQuery addTermFilter(String key, Object value) {

this .termFilter.put(key, value);

return this ;

}

public EsQuery addMustNotTermFilter(String key, Object value) {

this .mustNotTermFilter.put(key, value);

return this ;

}

public EsQuery addAllMustNotTermFilter(Map<String,Object> mustNot) {

if (mustNot != null && !mustNot.isEmpty()) {

this .mustNotTermFilter.putAll(mustNot);

}

return this ;

}

public EsQuery addShouldTermFilter(String key, Object value) {

this .shouldTermFilter.put(key, value);

return this ;

}

public EsQuery addAllShouldTermFilter(Map<String,Object> should) {

if (should != null && !should.isEmpty()) {

this .shouldTermFilter.putAll(should);

}

return this ;

}

public EsQuery addRangeFilter(String key, long gte, long lte){

this .rangeFilter.put(key, new Range(gte, lte));

return this ;

}

public EsQuery addMatchFilter(String key, Match value) {

this .matchFilter.put(key, value);

return this ;

}

public EsQuery addIncludeFields(List<String> includes) {

this .includes.addAll(includes);

return this ;

}

public EsQuery addExcludeFields(List<String> excludes) {

this .excludes.addAll(excludes);

return this ;

}

@Override

public String toString() {

return toJsonString();

}

public String toJsonString() {

Map<String, Object> finalQuery = new HashMap<>();

Map<String, Object> queryMap = new HashMap<>();

Map<String, Object> filteredMap = new HashMap<>();

Map<String, Object> filterMap = new HashMap<>();

Map<String, Object> boolMap = new HashMap<>();

List<Object> mustList = obtainTermFilterList( this .termFilter);

List<Object> mustNotList = obtainTermFilterList( this .mustNotTermFilter);

List<Object> shouldList = obtainTermFilterList( this .shouldTermFilter);

if (! this .rangeFilter.isEmpty()){

for (Map.Entry<String, Range> e: this .rangeFilter.entrySet()){

Map<String, Object> rangeMap = new HashMap<>();

Map<String, Object> rangeEntityMap = new HashMap<>();

rangeEntityMap.put(e.getKey(), e.getValue().toMap());

rangeMap.put(Constant.range, rangeEntityMap);

mustList.add(rangeMap);

}

}

if (! this .matchFilter.isEmpty()){

this .matchFilter.forEach(

(key, match) -> {

Map<String, String> matchEntityMap = new HashMap<>();

Map<String, Map> matchMap = new HashMap<>();

Map<String, Map> subMatchMap = new HashMap<>();

matchEntityMap.put(Constant.query, match.getQuery());

matchEntityMap.put(Constant.should_minum, match.getMinimumShouldMatch());

matchMap.put(key, matchEntityMap);

subMatchMap.put(Constant.match, matchMap);

mustList.add(subMatchMap);

});

}

boolMap.put(Constant.must, mustList);

if (!mustNotList.isEmpty())

boolMap.put(Constant.mustNot, mustNotList);

if (!shouldList.isEmpty()) {

// 有 minimum_should_match 不带过滤器

boolMap.put(Constant.should, shouldList);

boolMap.put(Constant.should_minum, shouldMatchMinimum);

queryMap.put(Constant.bool, boolMap);

}

else {

if (isNeedFilterLayer) {

filterMap.put(Constant.bool, boolMap);

filteredMap.put(Constant.filter, filterMap);

queryMap.put(Constant.filtered, filteredMap);

}

else {

queryMap.put(Constant.bool, boolMap);

}

}

finalQuery.put(Constant.query, queryMap);

Map<String, Object> orderMap = new HashMap<>();

Map<String, Object> orderItem = new HashMap<>();

if (order != null && orderBy != null ){

orderItem.put(Constant.order, this .order);

orderMap.put( this .orderBy, orderItem);

finalQuery.put(Constant.sort, orderMap);

}

Map<String,Object> source = new HashMap<>();

if (!includes.isEmpty()) {

source.put(Constant.includes, this .includes);

}

if (!excludes.isEmpty()) {

source.put(Constant.excludes, this .excludes);

}

if (!source.isEmpty()) {

finalQuery.put(Constant.source, source);

}

finalQuery.put(Constant.size, this .size);

if (from != null ) {

finalQuery.put(Constant.from, from.intValue());

}

return JSON.toJSONString(finalQuery);

}

public List<Object> obtainTermFilterList(Map<String, Object> termFilter) {

List<Object> termFilterList = new ArrayList<>();

for (Map.Entry<String, Object> e: termFilter.entrySet()){

Map<String, Object> termMap = new HashMap<>();

Map<String, Object> itemMap = new HashMap<>();

itemMap.put(e.getKey(), e.getValue());

if (e.getValue() instanceof List){

termMap.put(Constant.terms, itemMap);

} else {

termMap.put(Constant.term, itemMap);

}

termFilterList.add(termMap);

}

return termFilterList;

}

public String getOrderBy() {

return orderBy;

}

public void setOrderBy(String orderBy) {

this .orderBy = orderBy;

}

public String getOrder() {

return order;

}

public void setOrder(String order) {

this .order = order;

}

public int getSize() {

return size;

}

public void setSize( int size) {

this .size = size;

}

public Integer getFrom() {

return from;

}

public void setFrom(Integer from) {

this .from = from;

}

public Map<String, Object> getTermFilter() {

return Collections.unmodifiableMap(termFilter);

}

public Map<String, Range> getRangeFilter() {

return Collections.unmodifiableMap(rangeFilter);

}

public Map<String, Object> getMustNotTermFilter() {

return Collections.unmodifiableMap(mustNotTermFilter);

}

public Map<String, Object> getShouldTermFilter() {

return Collections.unmodifiableMap(shouldTermFilter);

}

public Map<String, Match> getMatchFilter() {

return matchFilter;

}

public void setShouldMatchMinimum(Integer shouldMatchMinimum) {

this .shouldMatchMinimum = shouldMatchMinimum;

}

public Integer getShouldMatchMinimum() {

return shouldMatchMinimum;

}

public Map<String, Object> getRangeMap(String key) {

return Collections.unmodifiableMap(rangeFilter.get(key).toMap());

}

public List<String> getIncludes() {

return Collections.unmodifiableList(includes);

}

public boolean isNeedFilterLayer() {

return isNeedFilterLayer;

}

public void setNeedFilterLayer( boolean needFilterLayer) {

isNeedFilterLayer = needFilterLayer;

}

@Override

public boolean equals(Object o) {

// for you to write

}

@Override

public int hashCode() {

// for you to write

}

小结

通过ES搜索示例,展示了如何运用注解自动化处理领域对象属性与底层ES存储字段之间的对应关系。实际上,如果想为应用对象或组件添加某种说明或注释,不妨先想想是否可以通过注解自动化处理。注解亦可用于框架自动处理对象与组件的集成。Spring框架的Resource, Component, AOP,以及 Plugin 化设计思想等都是好的应用例子。

文章来源:智云一二三科技

文章标题:使用 Java 注解自动化处理对应关系实现注释代码化

文章地址:https://www.zhihuclub.com/187504.shtml

关于作者: 智云科技

热门文章

评论已关闭

4条评论

  1. Corticosteroids and bisphosphonates, which work by inhibition of osteoclasts and antiresorptive effects, have been tried with promising results for rheumatologic manifestations of HOA Jha RM, Molyneaux BJ, Jackson TC, et al

  2. Start a savings account Invest in pet health insurance I get no money for telling you this, but I have been a policyholder with Nationwide Pet Insurance formerly VPI for over 25 years But there are a few things you can do to make healing a broken heart a little faster and easier

网站地图