列表查询的 GraphQL:一行代码终结你的 if-else 地狱

发布时间:2026/7/27 6:04:27
列表查询的 GraphQL:一行代码终结你的 if-else 地狱 一个后端工程师的自白为什么你写了 100 行 Java 代码其实只做了一件事——把前端传进来的几个参数拼成一条 SQL。一页纸的需求产品经理小王给我发了一张原型图。很普通的后台管理页面表格分页展示顶部四个筛选条件姓名、年龄区间、部门、入职时间可以按任意列排序底部统计总人数、平均年龄这个简单吧下午能上线吗他问。我说“能。”然后默默打开 IDEA开始写代码。如果你也用 MyBatis接下来 30 分钟你会做什么// 首先你要写一条动态 SQLselectidsearchUsersresultTypeUserVOSELECT u.*, d.name as deptName FROM user u LEFT JOIN dept d ON u.dept_id d.idwhereiftestname ! null and name ! AND u.name LIKE CONCAT(%, #{name}, %)/ififtestageMin ! nullAND u.age #{ageMin}/ififtestageMax ! nullAND u.agelt; #{ageMax}/ififtestdept ! null and dept ! AND d.name #{dept}/ififtestentryDateStart ! nullAND u.entry_date #{entryDateStart}/ififtestentryDateEnd ! nullAND u.entry_datelt; #{entryDateEnd}/if/whereiftestsortField ! null and sortOrder ! nullORDER BY ${sortField} ${sortOrder}/ifLIMIT #{offset}, #{size}/select还没完。你还需要一个统计 SQL、一个 Controller 方法解析参数、一个 Service 层做分页计算、一个 Mapper 接口、一个 VO 类专门承接查询结果——还要小心${}SQL 注入。需求只说了四个筛选条件代码已经快 100 行了。然后小王说“对了把部门筛选改成支持多选。还有加一个性别筛选。”你深吸一口气继续写。这不是 MyBatis 的问题——换 JPA你也不会更轻松SpecificationUserspec(root,query,cb)-{ListPredicatepredicatesnewArrayList();// 联表要查部门名先 JOIN department 表JoinUser,DepartmentdeptJoinroot.join(dept,JoinType.LEFT);if(StringUtils.isNotBlank(name)){predicates.add(cb.like(root.get(name),%name%));}if(ageMin!null){predicates.add(cb.ge(root.get(age),ageMin));}if(ageMax!null){predicates.add(cb.le(root.get(age),ageMax));}if(StringUtils.isNotBlank(dept)){predicates.add(cb.equal(deptJoin.get(name),dept));}// ... 同样的 if 地狱只是换了语法returncb.and(predicates.toArray(newPredicate[0]));};// 除此之外还有 排序、分页、总条数、字段统计每一项都都需要你不断的堆砌代码 ...MyBatis 在 XML 里写ifJPA 在 Java 里拼JoinPredicate。本质没变——你还是在用手工方式把参数翻译成查询条件。这个问题存在的唯一原因就是你把声明和执行混在了一起。如果换一种思想我们换一个视角看这个问题。数据库表你已经定义好了。前端要查什么用户说了算。那为什么中间的翻译工作——把 HTTP 参数翻译成 SQL——需要你一行一行写 if-else有没有可能让一个聪明的中间层来做这件事它知道你的实体有哪些字段 → 这就是检索边界前端传了哪些参数 → 这就是检索意图两者一结合 → 生成 SQL这个思路不是我的发明。在 API 领域它有一个如雷贯耳的名字GraphQL— 客户端指定要什么字段服务端返回什么字段。一次请求替代多次 REST 调用。那在列表查询领域能不能有同样的东西维度GraphQLBean Searcher领域API 数据查询数据库列表检索客户端控制什么返回哪些字段返回哪些字段 按什么筛选 按什么排序 分页多少协议POST GraphQL body标准 HTTP 参数GET/POST 均可核心思想声明你要什么数据声明检索边界参数驱动查询接入成本改 API 层、加 Schema一个依赖零代码改造一句话GraphQL 让前端在一次请求中自由控制返回数据Bean Searcher 让前端在一次请求中自由控制筛选、排序、分页和统计——用 REST 最熟悉的 URL 参数方式。列表检索领域的 GraphQL你只需要定义一个实体SearchBean(tablesuser u, dept d,whereu.dept_id d.id,autoMapTou)publicclassUserVO{privateLongid;privateStringname;privateIntegerage;privateStringgender;DbField(d.name)privateStringdeptName;privateLocalDateentryDate;// getters setters...}然后整个检索接口一行代码GetMapping(/user/search)publicSearchResultUserVOsearch(HttpServletRequestrequest){returnbeanSearcher.search(UserVO.class,MapUtils.flat(request.getParameterMap()));}前端直接 GET 请求GET /user/search?name张age-020age-130age-opbtsortageorderdesc这一行代码返回的数据长这样{dataList:[{id:1,name:张三,age:25,deptName:技术部,entryDate:2023-03-01},{id:2,name:张小明,age:28,deptName:产品部,entryDate:2022-11-15}],totalCount:47,summaries:[1350]}分页、联表、多条件筛选、排序、统计——一个接口全搞定。没有 XML。没有 if-else。没有 VO 转换代码。这就是 Bean Searcher——一个我用了三年、忍不住想安利给所有后端工程师的框架。它到底做了什么让我用一句话解释它的核心原理因为理解了这一点你就理解了它为什么能省掉 90% 的代码实体类声明检索边界HTTP 参数驱动查询逻辑。传统方式MyBatis / JPABean Searcher筛选条件怎么定义XML 里写if/ Java 里拼QueryWrapper参数名直接映射字段名加一个新筛选条件改 XML / 改 Java 代码 → 重新编译部署前端直接传新参数后端零改动多表联查手写 JOIN SQL实体类声明关联关系返回结果需要 VO 转换层SearchBean 就是 VO安全性自己写校验防注入、防大页、防深度偏移全部默认开启打个比方传统方式是命令式的——你告诉框架每一步怎么做。Bean Searcher 是声明式的——你声明能查什么实体定义边界然后前端通过参数表达想查什么驱动查询逻辑。你不是在写查询。你是在声明检索边界。一个会被问到的问题“这难道不会让前端传太多参数吗”这个问题我被问了无数次。答案是前端传多少参数只和产品需求的复杂度有关和后端用的什么框架毫无关系。如果产品只需要一个模糊搜索框前端就只传?name张不需要name-op和name-ic。你甚至可以零注解使用——一个单纯的 POJO字段名默认映射为数据库列名驼峰转下划线。记住一件事Annotation 是用来约束和精细化控制的不是必须的。单表实体什么注解都不用加天生可搜。它和 MyBatis/JPA 是敌人吗绝对不是。MyBatis 管增删改Bean Searcher 管列表查。各司其职和谐共存。就像 GraphQL 不是为了取代 REST 而生的——它只是让 API 查询更灵活。Bean Searcher 也不是为了取代 MyBatis——它只是让列表检索不再痛苦。什么时候用MyBatis / JPA增删改、事务性操作、复杂业务逻辑Bean Searcher后台管理列表、数据导出、报表查询、任何多条件动态筛选场景两者的关系互补不是替代。加一个依赖就够了。真实项目里的体验我在三个项目里深度使用 Bean Searcher分别是 Spring Boot 2/3/4 和 Solon 3/4最大的感受不是代码少了——而是思维方式变了。以前接到列表查询需求脑子里想的是这个 SQL 怎么写、参数怎么拼、排序怎么处理、分页传什么对象。现在接到列表查询需求脑子里想的是这个页面需要哪些字段它们来自哪些表哪些字段允许前端筛选。你不再是一个 SQL 拼接工。你是一个领域建模者。而当你把这种体验告诉同事时他们的第一反应通常是“这不就是……Java 后端版的 GraphQL”“对。”试试看如果你读到这里发现上面说的痛点都是你每天在经历的——那你应该试一下。不用重构项目不用替换 ORM不用改变任何架构。它是一个完全不侵入的框架和 MyBatis/JPA/Spring Data JDBC 都能共存。 完整文档 在线 Demo零部署体验⭐ GitHub Gitee如果你觉得这玩意确实解决了你的痛点点个 Star让更多被列表查询折磨的 Java 工程师看到它。毕竟——你把生命花在写 if-else 上不如花在更有价值的事情上。