资讯动态

⚡ Spring JDBC 完整体系 · 第 8 讲 · 查询四象限完备

发布时间:2026/8/6 4:25:56 来源:尧图企业网站定制
⚡ Spring JDBC 完整体系 · 第 8 讲 · 查询四象限完备相关开源地址核心框架源码https://gitee.com/gao_zhenzhong/simple-dao系统底座https://gitee.com/gao_zhenzhong/simple-dao-starter代码生成器https://gitee.com/gao_zhenzhong/simple-dao-coder实战案例本集源码https://gitee.com/gao_zhenzhong/simple-dao-demo 前置知识✅ 你只需要会❌ 你完全不需要会的Java 基础 Spring Boot 基本用法Spring 自动配置源码细节第 6-7 讲JdbcTemplate 增删改 命名参数MyBatis / Hibernate 任何知识基础 SQLSELECT、聚合函数连接池调优细节 全套教程总览20 讲从零到架构师篇章涵盖讲次核心目标开篇筑基第 1-3 讲认知梳理建立 Spring JDBC 的底层坐标系API 精讲第 4-7 讲全面掌握 JdbcTemplate 事务体系工具封装第 8-9 讲手搓条件工具 代码生成器从使用者变创造者认知升华第 10 讲模板确定性 vs AI 不确定性建立技术判断力高阶扩展第 11-17 讲多数据源、分库分表、分布式事务底层观测第 18-19 讲SQL 拦截、性能监控、连接池调优终极收尾第 20 讲全链路复盘 手搓持久层框架一、开篇 · 查询的四象限完备前两讲我们分别完成了JdbcTemplate的增删改操作第 6 讲和NamedParameterJdbcTemplate的命名参数第 7 讲。这一讲回归查询——SELECT 语句的结果形态分析。无论 SQL 写得多复杂——多表 JOIN、子查询、聚合函数、窗口函数——最终落到 Java 代码里查询结果只有四种形态。这就是所谓的四象限完备。具体是哪四种我们用一张图说清楚单列多列单行单行单列单行多列多行多行单列多行多列单行单列聚合函数COUNT、MAX、SUM等或唯一键查单个字段单行多列根据主键 ID 查询完整实体多行单列查某一列的所有值如所有用户名称列表多行多列最常规的列表查询返回多条完整记录这一讲的目标很明确四个方法四种形态覆盖所有查询场景没有遗漏没有重叠。二、项目结构与前置代码2.1 依赖与配置pom.xml沿用spring-boot-starter-jdbc H2 Lombok 的标准组合application.yml配置数据源并自动执行schema.sqllogback-spring.xml开启JdbcTemplate的 DEBUG 日志。细节同第 5-7 讲此处不再赘述。2.2 schema.sql · 建表与初始数据DROPTABLEIFEXISTSsys_user;CREATETABLEsys_user(user_idBIGINTAUTO_INCREMENTPRIMARYKEYCOMMENT主键,user_nameVARCHAR(50)COMMENT姓名,birthdayDATECOMMENT生日,genderTINYINTCOMMENT性别,ageINTCOMMENT年龄);INSERTINTOsys_user(user_name,birthday,gender,age)VALUES(郭靖,2018-07-13,1,25),(洪七公,2018-07-13,1,30),(王重阳,2018-07-13,1,40),(周伯通,2018-07-13,1,35),(李莫愁,2018-07-13,0,28);2.3 User.java · 实体类DataBuilderAllArgsConstructorNoArgsConstructorpublicclassUser{privateLonguserId;/* 主键 */privateStringuserName;/* 姓名 */privateLocalDatebirthday;/* 生日 */privateBytegender;/* 性别 */privateIntegerage;/* 年龄 */}三、demo01 · 单行多列 · findById场景根据主键 ID 查询单条记录返回完整实体对象。RepositorypublicclassUserDao{AutowiredprivateJdbcTemplatejdbc;publicvoiddemo01(){StringsqlSELECT user_id, user_name, birthday, gender, age FROM sys_user WHERE user_id ?;Useruserjdbc.queryForObject(sql,newBeanPropertyRowMapper(User.class),1L);log.info(user{},user);}}核心 API 拆解要素说明queryForObject期望结果集恰好一行的方法。多于一行会抛出IncorrectResultSizeDataAccessExceptionBeanPropertyRowMapper自动映射下划线字段user_id到驼峰属性userId第三个参数1L?占位符的实际参数值queryForObject与query的区别在于queryForObject底层调用query获取List然后断言list.size() 1若为 0 抛出EmptyResultDataAccessException若大于 1 抛出IncorrectResultSizeDataAccessException。因此只有确信结果集只有一行时如按主键查询才使用queryForObject。[INFO] userUser(userId1, userName郭靖, birthday2018-07-13, gender1, age25)四、demo02 · 单行单列 · 聚合统计场景聚合函数——COUNT、SUM、AVG、MAX、MIN返回单个值。publicvoiddemo02(){StringsqlSELECT COUNT(age) FROM sys_user WHERE user_id ?;Longcountjdbc.queryForObject(sql,Long.class,0L);log.info(count{},count);}核心 API 拆解要素说明queryForObject(sql, ClassT, args...)直接返回目标类型的单值无需RowMapperLong.class返回类型。支持Integer、Long、String、Date等基础类型/包装类型0L?占位符参数此处代表查询user_id 0的所有记录COUNT(age)会忽略age为NULL的行。如果表中数据为空COUNT返回0不会触发EmptyResultDataAccessException。但其他聚合函数如MAX、MIN在空表上会返回NULL映射为Long时需注意 NPE 问题建议使用Long.class而非long.class。[INFO] count5五、demo03 · 多行单列 · 取某一列场景只需要某一列的数据用于校验、去重、构造集合等场景。publicvoiddemo03(){StringsqlSELECT user_name FROM sys_user WHERE user_id ?;ListStringnamesjdbc.query(sql,newSingleColumnRowMapper(String.class),0L);log.info(names{},names);}核心 API 拆解要素说明query返回ListT零行时返回空列表不抛异常SingleColumnRowMapper专门处理单列多行结果集指定列的目标类型String.class列值映射到的目标类型SingleColumnRowMapper的底层逻辑对ResultSet的每一行只取第一列column index 1然后通过ConversionService或直接类型转换将 JDBC 类型映射为指定的 Java 类型。对于字符串列直接调用rs.getString(1)对于数值列调用rs.getObject(1)后转换为目标类型。零行时返回空列表与queryForObject不同——后者会抛异常。[INFO] names[郭靖, 洪七公, 王重阳, 周伯通, 李莫愁]六、demo04 · 多行多列 · 常规列表场景最常规的列表查询返回多条完整记录。PostConstructpublicvoiddemo04(){StringsqlSELECT user_id, user_name, birthday, gender, age FROM sys_user WHERE user_id ?;ListUserusersjdbc.query(sql,newBeanPropertyRowMapper(User.class),-1L);users.forEach(u-log.info(user{},u));}核心 API 拆解要素说明PostConstructBean 初始化后自动执行此处用于演示启动时打印数据query返回ListUser零行时返回空列表BeanPropertyRowMapper自动完成下划线转驼峰映射-1L参数值此处用于查询user_id -1即全部记录为什么用PostConstruct本讲的所有 demo 都是通过这个注解在 Spring 容器启动后自动执行的。这是一种演示技巧让你无需编写 Controller 或手动调用启动应用就能看到四种查询形态的输出结果。实际业务中查询方法通常由 Service 层调用而非在 DAO 中用PostConstruct驱动。[INFO] userUser(userId1, userName郭靖, birthday2018-07-13, gender1, age25) [INFO] userUser(userId2, userName洪七公, birthday2018-07-13, gender1, age30) [INFO] userUser(userId3, userName王重阳, birthday2018-07-13, gender1, age40) [INFO] userUser(userId4, userName周伯通, birthday2018-07-13, gender1, age35) [INFO] userUser(userId5, userName李莫愁, birthday2018-07-13, gender0, age28)七、四种查询 API 汇总象限APIRowMapper返回值零行时行为单行多列queryForObjectBeanPropertyRowMapper单个实体对象抛EmptyResultDataAccessException单行单列queryForObject无需 RowMapper传ClassT单个值Long/String等抛EmptyResultDataAccessException多行单列querySingleColumnRowMapperListT返回空列表多行多列queryBeanPropertyRowMapperListEntity返回空列表关键区别queryForObjectvsquery行为queryForObjectquery零行抛EmptyResultDataAccessException返回空列表一行返回该行返回包含一个元素的列表多行抛IncorrectResultSizeDataAccessException返回包含多个元素的列表⚠️实践中需要注意queryForObject并不能保证最多一行它只是期待一行。如果 SQL 条件写错导致返回多行比如按非唯一字段查询应用会直接抛出异常。因此queryForObject只适用于按主键或唯一约束查询的场景。对于其他单行查询优先用query 手动判断list.isEmpty()更加健壮。八、本讲核心总结核心要点说明四象限完备单行单列、单行多列、多行单列、多行多列覆盖所有查询场景queryForObjectBeanPropertyRowMapper单行多列 → 单个实体对象queryForObjectClassT单行单列 → 单个值聚合函数querySingleColumnRowMapper多行单列 →ListT某一列queryBeanPropertyRowMapper多行多列 →ListEntity最常规零行处理差异queryForObject抛异常query返回空列表九、下集预告第 9 讲将进入BaseCondition 条件封装——手搓条件类让动态 SQL 从散装if收敛为可复用的语义单元原生 JDBC 拼接动态 SQL 的痛点大量 if 判空冗余代码 BaseCondition 设计思路条件收集 参数收集 等值、区间、前后模糊、IN 条件的 API 设计 参数自动收集与 WHERE 语句生成

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价