Java , 写了一个生成模糊查询和时间区间查询 wrapper 的函数,有什么优化建议或是隐藏的坑吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Mrzhs
V2EX    Java

Java , 写了一个生成模糊查询和时间区间查询 wrapper 的函数,有什么优化建议或是隐藏的坑吗?

  •  
  •   Mrzhs 2024-04-15 16:16:01 +08:00 2406 次点击
    这是一个创建于 549 天前的主题,其中的信息可能已经有所发展或是发生改变。

    模糊查询大致思路是先遍历实体类,然后实体类如果有值,则获取它的 TableField 注解,获取字段名,然后拼接 wrapper 。
    时间查询的大体思路和上面差不多,前端传来 xxxStart 和 xxxxEnd, 首先保存到实体类中,然后再做字符拼接。 想起写这个的原因是因为基本每个前端 Table 都需要类似的查询,本来是写在 XML 文件里,但是每改一次字段都会联动着更改 XML ,太过繁琐,增加出错几率。 所以写了这么一个工具类来通过实体类动态拼接模糊查询。

    //拼接函数 /** * 通过实体类生成模糊搜索和日期区间查询的 wrapper * @param cls 类名 * @param entity 实体类 * @param orderByField 排序字段 * @param orderByAsc 是否升序 * @return QueryWrapper * @param <T> 实体类 */ public <T> QueryWrapper<T> generateFuzzySearchAndBetweenDateWrapper(Class<T> cls, T entity, String orderByField, Boolean orderByAsc) { QueryWrapper<T> wrapper = new QueryWrapper<>(); //先检查是否排序 if (!DataUtil.isEmpty(orderByField)) { wrapper.orderBy(true, orderByAsc, StringFormat.camelToUnderscore(orderByField)); } //再检查实体类是否为空 if (DataUtil.isEmpty(entity)) { return wrapper; } try { //遍历实体类的所有字段. getAllFields 方法获取不止当前类的字段, 还包括父类的字段 for (Field field: DataUtil.getAllFields(cls)) { //如果是 final, static, transient 修饰的字段, 则跳过. 一般用来排除 private static final long serialVersiOnUID= 1L; 序列化 id if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) { continue; } //获取字段的 get 方法 Method getFunc = cls.getMethod("get" + StringFormat.capitalize(field.getName())); Object value = getFunc.invoke(entity); //如果字段有 TableField 注解, 并且值不为空 if (field.isAnnotationPresent(TableField.class) && !DataUtil.isEmpty(value)) { //如果 TableField 注解的 exist 属性为 true, 则使用注解的 value 作为字段名 if (field.getAnnotation(TableField.class).exist()) { String column = field.getAnnotation(TableField.class).value(); wrapper.like(column, value); } else { //如果 TableField 注解的 exist 属性为 false, 证明可能是一个时间区域查询 String name = field.getName(); if (name.endsWith("Start")) { String column = name.substring(0, name.length() - 5); wrapper.ge(StringFormat.camelToUnderscore(column), value); } else if (name.endsWith("End")) { String column = name.substring(0, name.length() - 3); wrapper.le(StringFormat.camelToUnderscore(column), value); } } } } } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } return wrapper; } 
    //controller @GetMapping("select/page") public IPage<Company> selectPage(@RequestBody(required = false) Company company, @RequestParam(defaultValue = "1") Integer current, @RequestParam(defaultValue = "10") Integer size, @RequestParam(required = false)String orderByField, @RequestParam(defaultValue = "true")Boolean orderByAsc) { QueryWrapper<Company> companyQueryWrapper = generateFuzzySearchAndBetweenDateWrapper(Company.class, company, orderByField,orderByAsc); Page<Company> companyPage = new Page<>(); companyPage.setCurrent(current); companyPage.setSize(size); IPage<Company> companyIPage = companyService.page(companyPage, companyQueryWrapper); return companyIPage; } 

    已知的问题:
    1.Get 请求携带请求体,功能是可以实现,但似乎有些不合规矩,隐隐约约记得当时学 J2EE 的时候有讲过不要在 Get 请求携带请求体
    2.每个时间字段都需要增加两个虚拟属性,用于接受前端发来的时间范围数据

     @TableField("expire_date") @ApiModelProperty(value = "到期时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date expireDate; @TableField(exist = false) private String expireDateStart; @TableField(exist = false) private String expireDateEnd; 

    3.想实现数据字典的功能,但是感觉有点复杂, 首先需要调用其他 Service 来查询数据表,然后实体类还需要多加一个虚拟字段,用于存储数据字典对应的值。例如:

     @TableField("industry_type") @ApiModelProperty(value = "企业类型") private Integer industryType; @TableField(exist = false) private String industryTypeValue; 
    第 1 条附言    2024-04-16 09:57:23 +08:00

    又改了一下,貌似可以不加Start和End的虚拟字段了。

     public <T> QueryWrapper<T> generateFuzzySearchAndBetweenDateWrapper(Class<T> cls, Map<String, Object> param, String orderByField, Boolean orderByAsc) { QueryWrapper<T> wrapper = new QueryWrapper<>(); if (!DataUtil.isEmpty(orderByField)) { wrapper.orderBy(true, orderByAsc, StringFormat.camelToUnderscore(orderByField)); } if (DataUtil.isEmpty(param)) { return wrapper; } for (Field field : DataUtil.getAllFields(cls)) { //如果是final, static, transient修饰的字段, 则跳过. 一般用来排除private static final long serialVersiOnUID= 1L; 序列化id if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) || !field.isAnnotationPresent(TableField.class)) { continue; } String column = field.getAnnotation(TableField.class).value(); if (DataUtil.isEmpty(column)) { continue; } if (field.getType() == Date.class) { Object start = param.get(field.getName() + "Start"); if (start != null) { wrapper.ge(column, start); } Object end = param.get(field.getName() + "End"); if (end != null) { wrapper.le(column, end); } continue; } Object value = param.get(field.getName()); if (!DataUtil.isEmpty(value)) { if (field.getAnnotation(TableField.class).exist()) { wrapper.like(column, value); } } } return wrapper; } 
    7 条回复    2024-04-16 11:15:43 +08:00
    running17
        1
    running17  
       2024-04-15 16:29:01 +08:00
    俺的思路是做成一个新的注解,注解携带对应筛选的字段名和筛选的类型,直接解析这个注释来做这个逻辑;直接用 name 判断万一有业务是用 Start 和 End 的字段那就尴尬了;然后反射的操作记得用 map 之类的缓存起来,没有必要每次都去反射然后判断
    Mrzhs
        2
    Mrzhs  
    OP
       2024-04-15 16:43:17 +08:00
    @running17 虽然但是,如果通过注解来实现这两个字段的功能, 首先问题是, 如何将前端请求的 XXXStart 映射到注解里呢? Controller 貌似没有提供相关的功能
    jshfsym42
        3
    jshfsym42  
       2024-04-15 17:09:47 +08:00
    @Mrzhs 我的理解是,以时间范围查询为例,在实体类的字段上标注解,指示当前字段用于范围查询
    @RangeQuery
    private Date expireDate;
    前端直接 get 请求传实体类名 queryEntity ,和开始截止时间 startDate,endDate
    解析实体类名,拿到实体类,解析到实体类哪个字段有 @RangeQuery 注解用于时间范围查询,根据前端的开始截止时间拼接 wrapper 。
    Ayanokouji
        4
    Ayanokouji  
       2024-04-15 17:43:32 +08:00
    spring 支持用 object 来接收 get 的 query param
    Mrzhs
        5
    Mrzhs  
    OP
       2024-04-16 08:34:20 +08:00
    @Ayanokouji 是的. 但是不太清楚有没有什么弊端
    Ayanokouji
        6
    Ayanokouji  
       2024-04-16 08:55:20 +08:00   1
    @Mrzhs 这个有啥弊端,就是绑定,比如 validtor 注解,你自定义的注解也可以加到这个对象或对象的字段上
    SANJI59
        7
    SANJI59  
       2024-04-16 11:15:43 +08:00
    查询条件单独封装成实体,有同参数的定义成父类由子类查询体继承比如开始结束这种时间查询统一命名,然后时间属性这个字段可以由前端来传给你,尽量和库里时间字段保持一致,这样还支持多个时间字段的查询,不用定死。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     945 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 19:43 PVG 03:43 LAX 12:43 JFK 15:43
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86