Java 如何通用化查询逻辑应对复杂的查询需求? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jimisun
V2EX    问与答

Java 如何通用化查询逻辑应对复杂的查询需求?

  •  
  •   jimisun 2022-04-17 15:42:06 +08:00 2125 次点击
    这是一个创建于 1322 天前的主题,其中的信息可能已经有所发展或是发生改变。

    数据库有一些数据,如下模拟;

     ArrayList<User> objects = Lists.newArrayList(); objects.add(new User("zhangsan", "123456", 18, 1)); objects.add(new User("lisi", "123456", 22, 1)); objects.add(new User("wangwu", "123456", 30, 1)); objects.add(new User("zhaoliu", "123456", 43, 1)); objects.add(new User("xiaoli", "xiaoli67889", 16, 0)); objects.add(new User("xiaona", "nai324389", 22, 0)); 

    问题抛出

    针对这些数据的查询而言,通常会在 service 层定义许多查询接口,例如

    • 查询性别为男的用户
    • 查询性别为男年龄 20 以下的用户
    • 查询性别为男年龄 20 以下的并且默认密码是 123456 的用户
    • 随需求增加...

    面对需求的变化多样,总是需要增量添加 controller 方法,service 方法,甚至 dao 方法,如何解决这个问题呢?

    面向函数编程是否能满足呢?

    //dao public static List<User> getUserList() { ArrayList<User> objects = Lists.newArrayList(); objects.add(new User("zhangsan", "123456", 18, 1)); objects.add(new User("lisi", "123456", 22, 1)); objects.add(new User("wangwu", "123456", 30, 1)); objects.add(new User("zhaoliu", "123456", 43, 1)); objects.add(new User("xiaoli", "xiaoli67889", 16, 0)); objects.add(new User("xiaona", "nai324389", 22, 0)); return objects; } 
    //service public static Optional getUserListByPredicate(Predicate<User> predicate) { List<User> userList = UserDao.getUserList(); ArrayList<Object> resultList = Lists.newArrayList(); for (User user : userList) { if (predicate.test(user)) { resultList.add(user); } } return Optional.ofNullable(resultList); } 
    //controller public static void main(String[] args) { //查询性别为男,年龄 20 以下,并且密码为默认密码 123456 的用户 Optional<List> result = UserService.getUserListByPredicate((User user) -> { return user.getSex() == 1 && user.getAge() < 20 && user.getPassword().equals("123456"); }); result.get().stream().forEach(user -> System.out.println(user)); } 

    我的想法是,dao 层总是返回全量的数据(此处有问题性能问题),在 service 层对查询条件进行抽象,controller 只需要将查询条件传入即可。

    更进一步用 stream 处理

    //dao public static List<User> getUserList() { ArrayList<User> objects = Lists.newArrayList(); objects.add(new User("zhangsan", "123456", 18, 1)); objects.add(new User("lisi", "123456", 22, 1)); objects.add(new User("wangwu", "123456", 30, 1)); objects.add(new User("zhaoliu", "123456", 43, 1)); objects.add(new User("xiaoli", "xiaoli67889", 16, 0)); objects.add(new User("xiaona", "nai324389", 22, 0)); return objects; } 
    //controller 层 /** * 查询性别为男,年龄 20 以下,并且密码为默认密码 123456 的用户 * * @param args */ public static void main(String[] args) { List<User> userList = UserDao.getUserList(); userList.stream().filter( user -> user.getSex() == 1 && user.getAge() < 20 && user.getPassword().equals("123456") ).forEach(user -> System.out.println(user)); } 

    我的想法是,controller 总是获取所有的数据,针对流根据业务进行业务操作。

    以上两种方法是否可行? 问题?

    13 条回复    2022-04-18 14:07:12 +08:00
    PerFectTime
        1
    PerFectTime  
       2022-04-17 16:06:08 +08:00
    我们是维护了一个数据列表的列视图权限,在此基础上通过列视图的数据类型(bit/string/字典)由可配置的高级查询功能可以对每一列的情况进行筛选
    eggoxygen
        2
    eggoxygen  
       2022-04-17 16:26:25 +08:00 via iPhone
    根据 ORM 框架不同有不同解决方案吧。
    比如 JPA 的 Specification / QueryDsl 。
    定义好需要查询的 Condition / Criteria 。
    查询时传入即可。
    Leviathann
        3
    Leviathann  
       2022-04-17 16:26:32 +08:00
    为什么不根据查询条件动态生成 sql
    xiangyuecn
        4
    xiangyuecn  
       2022-04-17 17:16:50 +08:00
    多写一条 if else 就要被抓取坐牢
    micean
        5
    micean  
       2022-04-17 17:52:35 +08:00
    比如某个男人的 apijson……
    lower
        6
    lower  
       2022-04-17 17:57:56 +08:00   2
    直接让前端传 sql 语句吧,他们爱查啥查啥……
    EscYezi
        7
    EscYezi  
       2022-04-17 19:08:36 +08:00 via iPhone   1
    看场景都是同一些数据同一些字段数值不同。controller 层只需要一个接口,定义一个含有多个字段的 vo ,本次查询用不到的字段和前端协商一个默认值就 ok
    dao 层 mybatis 的 xml 文件中 if 和 choose 根据各种情况拼接查询条件; mybatis-plus 也可以在拼接查询条件时增加 bool 参数指定是否拼接
    全部查询出来数据少还好,多起来一次拿几十万数据出来,数据库和 java 服务压力都很大
    letitbesqzr
        8
    letitbesqzr  
       2022-04-17 22:05:26 +08:00
    试试用 Aviator 之类的表达式解析工具? 让前端传表达式
    rehoni
        9
    rehoni  
       2022-04-18 08:29:55 +08:00
    定义一个通用的查询过滤器 Qo 数组,用作查询条件的拼接。当数组为空时,默认查全部 sql 为 select*;数组中每个对象对应一个条件,如年龄 20 以下、默认密码是 123456 ,拼接出来 sql 就是 select * where age < 20 ,pwd = ‘123456’; Qo 对象的 JSON 很明显包含字段名 age ,条件<,值 20 等,这是最基础的,然后在此 JSON 基础上还可以做一些拓展,如复杂字段建立驼峰关系或者建立映射,条件提供枚举,时间类型提供格式化,整体条件提供前端解决方案如指定为下拉框、时间选择器,再者就是可以利用框架特性来进行条件 Qo 的封装来实现通用效果,如 mybatis-plus 的条件构造器。
    whatevers
        10
    whatevers  
       2022-04-18 10:03:36 +08:00
    单表查询用 Mybatis 逆向工程生成 Mapper ,前端传参生成动态 sql
    MoozLee
        11/span>
    MoozLee  
       2022-04-18 10:48:50 +08:00
    graphql?
    BrbiwsFtd9zDGZqB
        12
    BrbiwsFtd9zDGZqB  
       2022-04-18 11:17:51 +08:00
    1. mybatis, 用 myabtis-plus 的 QueryWrapper, 动态拼接一下查询条件.
    https://baomidou.com/pages/10c804

    2. jpa, 看文档 4.8.2 那一节, Querydsl Web Support.
    https://docs.spring.io/spring-data/jpa/docs/current/reference/html


    实现后接口大概是这样: http://your.api/user?name=Alice&page=0&size=20&sort=age,asc
    aguesuka
        13
    aguesuka  
       2022-04-18 14:07:12 +08:00
    前端到后端用 luence 语法, 后端映射到 sql
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     799 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 20:51 PVG 04:51 LAX 12:51 JFK 15:51
    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