Use reflection to dynamically construct wrapper conditions
Entity class
/** * api list query entity class * *@authorwys */ @Data public class SysApiQuery { private Long id; /** * api name */ @Query(type = QueryTypeEnum.EQUAL) private String apiName; /** * api path */ @Query(type = QueryTypeEnum.EQUAL) private String apiUrl; /** Starting time */ @JsonIgnore private String beginTime; /** End Time */ @JsonIgnore private String endTime; }
QueryTypeEnum enumeration class
@Getter @RequiredArgsConstructor public enum QueryTypeEnum { /** * Equivalent query, for example: WHERE `age` = 18 */ EQUAL(1, "="), /** * Non-equivalent query, for example: WHERE `age` != 18 */ NOT_EQUAL(2, "!="), /** * Greater than query, for example: WHERE `age` > 18 */ GREATER_THAN(3, ">"), /** * Less than query, for example: WHERE `age` < 18 */ LESS_THAN(4, "<"), /** * Greater than or equal to query, for example: WHERE `age` >= 18 */ GREATER_THAN_OR_EQUAL(5, ">="), /** * Less than or equal to query, for example: WHERE `age` <= 18 */ LESS_THAN_OR_EQUAL(6, "<="), /** * Range query, for example: WHERE `age` BETWEEN 10 AND 18 */ BETWEEN(7, "BETWEEN"), /** * Left fuzzy query, for example: WHERE `nickname` LIKE '%s' */ LEFT_LIKE(8, "LIKE '%s'"), /** * Fuzzy query, for example: WHERE `nickname` LIKE '%s%' */ INNER_LIKE(9, "LIKE '%s%'"), /** * Right fuzzy query, for example: WHERE `nickname` LIKE 's%' */ RIGHT_LIKE(10, "LIKE 's%'"), /** * Contains queries, for example: WHERE `age` IN (10, 20, 30) */ IN(11, "IN"), /** * Does not contain queries, for example: WHERE `age` NOT IN (20, 30) */ NOT_IN(12, "NOT IN"), /** * Empty query, for example: WHERE `email` IS NULL */ IS_NULL(13, "IS NULL"), /** * Non-null query, for example: WHERE `email` IS NOT NULL */ IS_NOT_NULL(14, "IS NOT NULL"),; private final Integer value; private final String description; }
Query custom annotations
import com.chinaunicom.common.enums.QueryTypeEnum; import java.lang.annotation.*; /** * Query annotations * *@author Zheng Jie(ELADMIN) * @author Charles7c * @since 2023/1/15 18:01 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Query { /** * Attribute name (default is consistent with the name of the attribute using this annotation) */ String property() default ""; /** * Query type (equivalence query, fuzzy query, range query, etc.) */ QueryTypeEnum type() default QueryTypeEnum.EQUAL; /** * Multi-attribute fuzzy query, only supports String type attributes, separate multiple attributes with commas * <p> * For example: @Query(blurry = "username,email") means fuzzy query based on username and email *</p> */ String blurry() default ""; }
QueryHelper Query Assistant
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.chinaunicom.common.annotation.Query; import com.chinaunicom.common.enums.QueryTypeEnum; import com.chinaunicom.common.utils.reflect.ReflectUtils; import com.chinaunicom.common.validate.ValidationUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** * Query assistant * *@author Zheng Jie(ELADMIN) * @author Charles7c * @since 2023/1/15 18:17 */ @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class QueryHelper { /** * Construct MyBatis Plus query condition encapsulation object based on query conditions * * @param query * Query conditions * @param <Q> * Query condition data type * @param <R> * Query data type * @return MyBatis Plus query condition encapsulation object */ public static <Q, R> QueryWrapper<R> build(Q query) { QueryWrapper<R> queryWrapper = new QueryWrapper<>(); // No query conditions, return directly if (null == query) { return queryWrapper; } // Get all fields in the query conditions List<Field> fieldList = ReflectUtils.getNonStaticFields(query.getClass()); fieldList.forEach(field -> buildQuery(query, field, queryWrapper)); return queryWrapper; } /** * Construct MyBatis Plus query condition encapsulation object * * @param query * Query conditions * @param field * field * @param queryWrapper * MyBatis Plus query condition encapsulation object * @param <Q> * Query condition data type * @param <R> * Query data type */ private static <Q, R> void buildQuery(Q query, Field field, QueryWrapper<R> queryWrapper) { boolean accessible = field.isAccessible(); try { field.setAccessible(true); // Without @Query, return directly Query queryAnnotation = field.getAnnotation(Query.class); if (null == queryAnnotation) { return; } // If the field value is empty, return directly Object fieldValue = field.get(query); if (ObjectUtil.isEmpty(fieldValue)) { return; } // Parse query conditions parse(queryAnnotation, field.getName(), fieldValue, queryWrapper); } catch (Exception e) { log.error("Build query occurred an error: {}. Query: {}, Field: {}.", e.getMessage(), query, field, e); } finally { field.setAccessible(accessible); } } /** * Parse query conditions * * @param queryAnnotation * Query annotations * @param fieldName *Field name * @param fieldValue *Field value * @param queryWrapper * MyBatis Plus query condition encapsulation object * @param <R> * Query data type */ private static <R> void parse(Query queryAnnotation, String fieldName, Object fieldValue, QueryWrapper<R> queryWrapper) { // Parse multi-attribute fuzzy query // If a multi-attribute fuzzy query is set, split the attributes for conditional splicing. String blurry = queryAnnotation.blurry(); if (StrUtil.isNotBlank(blurry)) { String[] propertyArr = blurry.split(","); queryWrapper.and(wrapper -> { for (String property : propertyArr) { wrapper.or().like(StrUtil.toUnderlineCase(property), fieldValue); } }); return; } // Parse a single attribute query // If the attribute name is not specified separately, it will be consistent with the name of the attribute using this annotation. // Note: The columns in the database specification are named using the underscore connection method, and the variables in the program specification are named using the camel case method. String property = queryAnnotation.property(); String columnName = StrUtil.toUnderlineCase(StrUtil.blankToDefault(property, fieldName)); QueryTypeEnum queryType = queryAnnotation.type(); switch (queryType) { case EQUAL: queryWrapper.eq(columnName, fieldValue); break; case NOT_EQUAL: queryWrapper.ne(columnName, fieldValue); break; case GREATER_THAN: queryWrapper.gt(columnName, fieldValue); break; case LESS_THAN: queryWrapper.lt(columnName, fieldValue); break; case GREATER_THAN_OR_EQUAL: queryWrapper.ge(columnName, fieldValue); break; case LESS_THAN_OR_EQUAL: queryWrapper.le(columnName, fieldValue); break; case BETWEEN: List<Object> between = new ArrayList<>((List<Object>)fieldValue); ValidationUtils.throwIf(between.size() != 2, "[{}] must be a range", fieldName); queryWrapper.between(columnName, between.get(0), between.get(1)); break; case LEFT_LIKE: queryWrapper.likeLeft(columnName, fieldValue); break; case INNER_LIKE: queryWrapper.like(columnName, fieldValue); break; case RIGHT_LIKE: queryWrapper.likeRight(columnName, fieldValue); break; case IN: ValidationUtils.throwIfEmpty(fieldValue, "[{}] cannot be empty", fieldName); queryWrapper.in(columnName, (List<Object>)fieldValue); break; case NOT_IN: ValidationUtils.throwIfEmpty(fieldValue, "[{}] cannot be empty", fieldName); queryWrapper.notIn(columnName, (List<Object>)fieldValue); break; case IS_NULL: queryWrapper.isNull(columnName); break; case IS_NOT_NULL: queryWrapper.isNotNull(columnName); break; default: throw new IllegalArgumentException(String.format("[%s] query type is not supported yet", queryType)); } } }
PageDataVO structure encapsulation
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.Data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Pagination information * * @param <V> * List data type * @author Charles7c * @since 2023/1/14 23:40 */ @Data public class PageDataVO<V> implements Serializable { private static final long serialVersionUID = 1L; /** * List data */ private List<V> list; /** * total */ private long total; /** * Build paging information based on MyBatis Plus paging data and convert source data into specified type data * * @param page * MyBatis Plus paging data * @param targetClass * Target type Class object * @param <T> * Source list data type * @param <V> * Target list data type * @return paging information */ public static <T, V> PageDataVO<V> build(IPage<T> page, Class<V> targetClass) { if (null == page) { return empty(); } PageDataVO<V> pageDataVO = new PageDataVO<>(); pageDataVO.setList(BeanUtil.copyToList(page.getRecords(), targetClass)); pageDataVO.setTotal(page.getTotal()); return pageDataVO; } /** * Based on MyBatis Plus and convert source data into specified type data * * @param * * @param targetClass * Target type Class object * @param <T> * Source list data type * @param <V> * Target list data type * @return paging information */ public static <T, V> List<V> build(List<T> list, Class<V> targetClass) { if (null == list) { return new ArrayList<>(); } List<V> toList = BeanUtil.copyToList(list, targetClass); return toList; } /** * Build paging information based on MyBatis Plus paging data * * @param page * MyBatis Plus paging data * @param <V> * List data type * @return paging information */ public static <V> PageDataVO<V> build(IPage<V> page) { if (null == page) { return empty(); } PageDataVO<V> pageDataVO = new PageDataVO<>(); pageDataVO.setList(page.getRecords()); pageDataVO.setTotal(page.getTotal()); return pageDataVO; } /** * Build paging information based on list data * * @param page *Page number * @param size *Number of items per page * @param list * List data * @param <V> * List data type * @return paging information */ public static <V> PageDataVO<V> build(int page, int size, List<V> list) { if (CollUtil.isEmpty(list)) { return empty(); } PageDataVO<V> pageDataVO = new PageDataVO<>(); pageDataVO.setTotal(list.size()); //Page the list data int fromIndex = (page - 1) * size; int toIndex = page * size + size; if (fromIndex > list.size()) { pageDataVO.setList(new ArrayList<>(0)); } else if (toIndex >= list.size()) { pageDataVO.setList(list.subList(fromIndex, list.size())); } else { pageDataVO.setList(list.subList(fromIndex, toIndex)); } return pageDataVO; } /** * Empty paging information * * @param <V> * List data type * @return paging information */ private static <V> PageDataVO<V> empty() { PageDataVO<V> pageDataVO = new PageDataVO<>(); pageDataVO.setList(new ArrayList<>(0)); return pageDataVO; } }
service implementation
@Override public List<SysApiVo> pageList(SysApiQuery apiQuery) { //Construct MyBatis Plus query condition encapsulation object based on query conditions QueryWrapper<SysApi> queryWrapper = QueryHelper.build(apiQuery); List<SysApi> apiList = baseMapper.selectList(queryWrapper); // Based on MyBatis Plus and convert source data to specified type data List<SysApiVo> list = PageDataVO.build(apiList, SysApiVo.class); return list; }