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中增加一处属性和注解即可,不用再复制一段雷同的代码

相关内容

热门资讯

长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
亚克力是什么 亚克力制品对人体... 亚克力其特殊的物理性能使其可以承载超高水体压力,从而让超大水体成为可能。水族领域亚克力板材一直是海洋...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
如何吸引顾客 怎样吸引顾客加入... 如何激活群的顾客活跃度,是商家在运营过程中绕不开的事情。当新用户来到商家的私域流量群时,商家一般会采...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...