Mybatis+自定义注解,优雅的实现条件查询
创始人
2024-04-27 15:05:04

Springboot+Mybatis实现条件查询可以这样实现

@Data
@NoArgsConstructor
@ApiModel("查询DTO")
public class QueryDTO  {@ApiModelProperty("名称")private String name;@ApiModelProperty("类型")private String type;@ApiModelProperty("描述")private String description;}

Controller代码如下

    @GetMapping("/list")@ApiOperation(value = "列表查询")public Result list(@ApiParam QueryDTO request){QueryWrapper queryWrapper = new QueryWrapper<>();if(request.getName()!=null){queryWrapper.like("name",request.getName());}if(request.getType()!=null){queryWrapper.eq("type",request.getType());}if(request.getDescription()!=null){queryWrapper.eq("descrption",request.getDescription());}List list = service.list(queryWrapper);return Result.success(list);}

可见,每多一个条件,就要多写一段 queryWrapper.eq...这样类似的代码

使用自定义注解可以更优雅的实现条件查询

添加一个自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Query {/*** 对应数据库字段,为空取实体属性名 驼峰转下划线*/String column() default "";/*** 另一个类中的属性名称,支持多级获取,以小数点隔开*/String targetAttr() default "";/*** 查询类型*/Type type() default Type.EQUAL;enum Type {/*** 相等*/EQUAL((queryWrapper, filedColumn, val) -> {queryWrapper.eq(filedColumn, val);}),/*** 大于等于*/GREATER_THAN((queryWrapper, filedColumn, val) -> {queryWrapper.ge(filedColumn, val);}),/*** 大于*/GREATER_THAN_NQ((queryWrapper, filedColumn, val) -> {queryWrapper.gt(filedColumn, val);}),/*** 小于等于*/LESS_THAN((queryWrapper, filedColumn, val) -> {queryWrapper.le(filedColumn, val);}),/*** 小于*/LESS_THAN_NQ((queryWrapper, filedColumn, val) -> {queryWrapper.lt(filedColumn, val);}),/*** 不等于*/NOT_EQUAL((queryWrapper, filedColumn, val) -> {queryWrapper.ne(filedColumn, val);}),/*** 不为空*/NOT_NULL((queryWrapper, filedColumn, val) -> {queryWrapper.isNotNull(filedColumn);}),/*** 中模糊查询
* like %str%*/LIKE((queryWrapper, filedColumn, val) -> {queryWrapper.like(filedColumn, val);}),/*** 左模糊查询*/LEFT_LIKE((queryWrapper, filedColumn, val) -> {queryWrapper.likeLeft(filedColumn, val);}),/*** 右模糊查询*/RIGHT_LIKE((queryWrapper, filedColumn, val) -> {queryWrapper.likeRight(filedColumn, val);}),/*** 包含*/IN((queryWrapper, filedColumn, val) -> {queryWrapper.in(filedColumn, (Collection) val);}),/*** JSON数组中是否包含*/JSON_ARRAY_CONTAINS((queryWrapper, filedColumn, val) -> {queryWrapper.apply("JSON_CONTAINS(" + filedColumn + ",json_array({0}) )", val);}),;private TriConsumer consumer;Type(TriConsumer consumer) {this.consumer = consumer;}public void buildQuery(QueryWrapper t, String field, Object val) {consumer.accept(t, field, val);}} }
如果字段名与属性名相同或相匹配(驼峰转下划线 如:createTime-> create_time 表示字段匹配),不用标注column字段
如果字段名与属性名不同且不匹配,如属性名为 searchValue,需要查询的字段是name,则需要增加column字段,@Query(column="name")默认使用=查询,如果要使用like或其它查询类型,则需要标注type字段,选择相应的type即可,如: @Query(type=Query.Type.LIKE) 
每个type会生成什么样的QueryWrapper语句,可以参考Type枚举类的方法

再添加一个处理注解的工具类

@Slf4j
public class QueryUtil {public static  QueryWrapper setQueryWrapper(T bean) {QueryWrapper queryWrapper = new QueryWrapper<>();if (bean == null) {return queryWrapper;}try {List fields = getFields(bean.getClass());for (Object[] os : fields) {Field field = (Field) os[0];Query q = (Query) os[1];Object val = getTargetValue(bean, field, q);if (val == null) {continue;}String filedColumn = StringUtils.isBlank(q.column()) ? StringUtils.toUnderScoreCase(field.getName()) : q.column();q.type().buildQuery(queryWrapper,filedColumn,val);}} catch (Exception e) {log.error("设置查询条件失败{}", e);}return queryWrapper;}/*** 获取bean值** @param vo* @param field* @return* @throws Exception*/public static Object getObjectValue(Object vo, Field field) throws Exception {Object o = field.get(vo);if (o == null) {return null;}//空字符串转换为null对象if (field.getGenericType().toString().equals("class java.lang.String")) {String val = (String) o;if (StringUtils.isBlank(val)) {o = null;}} return o;}/*** 获取bean中的属性值** @param vo    实体对象* @param field 字段* @param query 注解* @return 最终的属性值* @throws Exception*/private static Object getTargetValue(Object vo, Field field, Query query) throws Exception {Object o = getObjectValue(vo, field);if (o == null) {return o;}if (StringUtils.isNotEmpty(query.targetAttr())) {String target = query.targetAttr();if (target.contains(".")) {String[] targets = target.split("[.]");for (String name : targets) {o = getValue(o, name);}} else {o = getValue(o, target);}}return o;}/*** 以类的属性的get方法方法形式获取值** @param o* @param name* @return value* @throws Exception*/private static Object getValue(Object o, String name) throws Exception {if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) {if (o instanceof Map) {Map objectMap = BeanUtils.beanToMap(o);if (objectMap.containsKey(name)) {return objectMap.get(name);}return null;} else {Class clazz = o.getClass();Field field = clazz.getDeclaredField(name);field.setAccessible(true);o = getObjectValue(o, field);}}return o;}/*** 获取字段注解信息*/public static List getFields(Class clazz) {List fields = new ArrayList();List tempFields = new ArrayList<>();tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));for (Field field : tempFields) {// 单注解if (field.isAnnotationPresent(Query.class)) {Query attr = field.getAnnotation(Query.class);if (attr != null) {field.setAccessible(true);fields.add(new Object[]{field, attr});}}// 多注解if (field.isAnnotationPresent(Queries.class)) {Queries attrs = field.getAnnotation(Queries.class);Query[] queries = attrs.value();for (Query attr : queries) {if (attr != null) {field.setAccessible(true);fields.add(new Object[]{field, attr});}}}}return fields;}}

在查询的DTO类中使用注解

@Data
@NoArgsConstructor
@ApiModel("查询DTO")
public class QueryDTO  {/*** 名称*/@ApiModelProperty("名称")@Query(type = Query.Type.LIKE) //不是用等于查找条件,需要指定typeprivate String name;/*** 类型*/@ApiModelProperty("类型")@Query  //表字段与属性名相匹配,且是用等于查找的,只需要这样的简单注解即可private String type;/*** 描述*/@ApiModelProperty("描述")@Query(column="description") //如果字段名与属性名不匹配,则添加column字段值指定表字段private String description;}

Controller类中使用如下:

    @GetMapping("/list")@ApiOperation(value = "列表查询")public Result list(@ApiParam QueryDTO request){QueryWrapper queryWrapper = QueryUtil.setQueryWrapper(request);List list = service.list(queryWrapper);return Result.success(list);}

Controller中的代码更简洁了,如果需要增加一个条件,只需要在QueryDTO中增加一处属性和注解即可,不用再复制一段雷同的代码

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...