为什么查询接口一开始就要传入实体,而不是参数?
一个被严重低估的 Java 工程设计问题在很多 Java 项目中我们经常能看到这样的代码演进过程// 初版getOrder(LongorderId);// 第二版getOrder(LongorderId,Integerstatus);// 第三版getOrder(LongorderId,Integerstatus,LocalDateTimestartTime);// 第四版崩了getOrder(LongorderId,Integerstatus,LocalDateTimestartTime,LocalDateTimeendTime,LonguserId);直到有一天有人站出来说了一句“我们直接传一个查询对象吧。”很多人听完的第一反应是查询而已传这么重的对象是不是过度设计直接参数不是更清晰吗DTO / VO / Query 到底有什么区别但在真正做过复杂系统、长期维护系统的人眼里查询传实体往往不是设计洁癖而是工程理性。本文将从 工程演进、代码稳定性、可扩展性、分层解耦 四个角度把这个问题一次性讲透。一、从“参数爆炸”开始说起先看一个真实的订单查询接口。1️⃣ 参数式查询初期publicListOrderqueryOrder(LongorderId,LonguserId,Integerstatus,LocalDateTimestartTime,LocalDateTimeendTime){...}看起来没什么问题但你很快会遇到参数越来越多参数顺序极易写错新增条件 修改所有调用方MyBatis / Controller / Service 层同时改 接口一旦暴露就很难再动二、查询实体Query Object到底解决了什么1️⃣ 查询条件是“一个整体”而不是一堆零散参数在业务语义上“订单查询”本身就是一个概念而不是“orderId userId status timeRange 的组合体”于是我们引入查询实体Query Object。三、标准做法Query Object1️⃣ 定义查询对象/** * 订单查询条件 */publicclassOrderQuery{/** 订单号 */privateLongorderId;/** 用户ID */privateLonguserId;/** 订单状态 */privateIntegerstatus;/** 创建时间开始 */privateLocalDateTimestartTime;/** 创建时间结束 */privateLocalDateTimeendTime;// getter / setter} 注意这是 Query不是 Entity不是 DTO2️⃣ Service 层接口立刻变得稳定publicListOrderqueryOrder(OrderQueryquery){returnorderMapper.selectByCondition(query);}此时你会发现一个关键变化接口签名稳定了以后新增条件privateIntegerpayType;privateLongmerchantId;不改 Service 方法不改 Controller不影响历史调用方四、MyBatis 中 Query Object 的真正威力Mapper 接口ListOrderselectByCondition(OrderQueryquery);Mapper XML核心价值selectidselectByConditionresultTypeOrderSELECT * FROM orderswhereiftestorderId ! nullAND order_id #{orderId}/ififtestuserId ! nullAND user_id #{userId}/ififteststatus ! nullAND status #{status}/ififteststartTime ! nullAND create_timegt; #{startTime}/ififtestendTime ! nullAND create_timelt; #{endTime}/if/where/select Query Object 天然的动态 SQL 载体五、为什么不推荐 Controller 直接传一堆参数❌ Controller 参数式写法问题多GetMapping(/list)publicListOrderlist(RequestParam(requiredfalse)LongorderId,RequestParam(requiredfalse)Integerstatus,RequestParam(requiredfalse)LonguserId){...}问题参数多 → 方法臃肿参数校验零散不利于复用✅ Controller 直接接收 Query Object推荐GetMapping(/list)publicListOrderlist(OrderQueryquery){returnorderService.queryOrder(query);}Spring MVC 会自动完成参数绑定/list?orderId1status2 自动注入到OrderQuery六、从架构角度看这是“边界清晰”的体现查询实体的本质是把“变化点”封装起来维度参数式Query Object扩展性❌ 差✅ 极强接口稳定性❌ 易变✅ 稳定代码可读性❌ 低✅ 高动态 SQL❌ 繁琐✅ 天然支持复杂查询❌ 痛苦✅ 友好七、进阶Query Page Sort 的标准模型1️⃣ 基础查询对象publicclassBaseQuery{privateIntegerpageNum1;privateIntegerpageSize20;}2️⃣ 订单查询继承publicclassOrderQueryextendsBaseQuery{privateLongorderId;privateIntegerstatus;}3️⃣ 接口永远不再变化publicPageResultOrderqueryOrder(OrderQueryquery){...} 这就是大型系统为什么“一开始就传实体”八、什么时候不该用 Query Object不是所有地方都必须用。❌ 不适合的场景极简单查询如getById(id)底层工具方法极高频、对 GC 极度敏感的代码段极少✅ 适合的场景列表查询条件组合查询后台管理系统需要长期维护的业务接口九、一句话总结查询传实体不是为了现在而是为了三个月、半年、一年后的系统。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2428257.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!