跳到主要内容
提示

本文内容结构参考了英文版文档,便于中英文对照。

查询

JdbcTemplate 提供多种只读查询 API。大多数接口接受 SQL 字符串和参数(对象或数组),返回由结果集构建的对象;部分接口支持回调/映射器以自定义处理过程。

查询结果集

查询一个对象或 Map 集合并返回。

// 原始 SQL
List<User> users = jdbc.queryForList(
"select * from users where id > 2", User.class);

// 位置参数
Object[] args = new Object[]{ 2 };
List<User> result = jdbc.queryForList(
"select * from users where id > ?", args, User.class);

// 名称参数
Map<String, Object> args = CollectionUtils.asMap("id", 2);
List<User> result = jdbc.queryForList(
"select * from users where id > :id", args, User.class);

查询对象

返回映射到指定类型的单行。常见场景:聚合查询、主键查找。若结果为多行会抛出异常。

// 原始 SQL
User user = jdbc.queryForObject(
"select * from users where id = 2", User.class);

// 位置参数
Object[] args = new Object[]{ 2 };
User user = jdbc.queryForObject(
"select * from users where id = ?", args, User.class);

// 名称参数
Map<String, Object> args = CollectionUtils.asMap("id", 2);
User user = jdbc.queryForObject(
"select * from users where id = :id", args, User.class);

查询键值对

键值对是将查询到的结果集将第一个列和第二个列分别作为 Map 的 Key 和 Value 将整个结果集存储为一个 Map 类型。

// 原始 SQL
Map<Long, String> userMap = jdbc.queryForPairs(
"select uid, name from user where age > 2", Long.class, String.class);

// 位置参数
Object[] args = new Object[]{ 2 };
Map<Long, String> userMap = jdbc.queryForPairs(
"select uid, name from user where age > ?", Long.class, String.class, args);

// 名称参数
Map<String, Object> args = CollectionUtils.asMap("age", 2);
Map<Long, String> userMap = jdbc.queryForPairs(
"select uid, name from user where age > :age", Long.class, String.class, args);

查询值

查询值区别于查询对象是在于值是可映射的最小类型单元,可以是 TypeHandlerRegistry 注册器中的类型。如:StirngIntDate 等。查询值通常有两种情况:

  • 查询值列表,例如获取符合条件的对象 ID 集合。
  • 查询聚合汇总信息,例如统计总数,求平均值等。
// 原始 SQL
long count = jdbc.queryForLong(
"select count(*) from user where age > 2");

// 位置参数
Object[] args = new Object[]{ 2 };
long count = jdbc.queryForLong(
"select count(*) from user where age > ?", args);

// 名称参数
Map<String, Object> args = CollectionUtils.asMap("age", 2);
long count = jdbc.queryForLong(
"select count(*) from user where age > :age", args);
提示
  • jdbc.queryForObject("select count(*) from users", Integer.class); // 具有等效能力

使用 ResultSetExtractor

使用 ResultSetExtractor 接口是最灵活处理结果集的方式,用户需要自行实现执行从 ResultSet 中提取结果的实际工作,同时无需担心异常处理。

// 原始 SQL
long count = jdbc.queryForLong(
"select count(*) from user where age > 2");

// 位置参数
Object[] args = new Object[]{ 2 };
long count = jdbc.queryForLong(
"select count(*) from user where age > ?", args);

// 名称参数
Map<String, Object> args = CollectionUtils.asMap("age", 2);
long count = jdbc.queryForLong(
"select count(*) from user where age > :age", args);
提示
  • jdbc.queryForObject("select count(*) from users", Integer.class); // 具有等效能力

流式查询

针对超大结果集,逐行流式处理以节省内存;每行在回调中即时处理。

// 流式消费数据
RowCallbackHandler handler = (rs, rowNum) -> {
// 处理一行
};

// 原始 SQL
jdbc.query("select * from user where age > 2", handler);

// 位置参数
Object[] args = new Object[]{ 2 };
jdbc.query("select * from user where age > ?", args, handler);

// 名称参数
Map<String, Object> args = CollectionUtils.asMap("age", 2);
jdbc.query("select * from user where age > :age", args, handler);

Map 大小写敏感性

默认情况下以 Map 为数据存储结构时查询返回的 key 对于大小写不敏感。

有如下查询
List<Map<String, Object>> result = jdbc.queryForList("select * from users");

users 表在相同表结构和数据在不同数据库中执行结果会有不同,例如:

  • 在 Oracle 返回的列名默认:大写
  • 在 MySQL 返回的列名默认:小写

dbVisitor 默认会采用 LinkedCaseInsensitiveMap 工具类以不区分大小写的方式存储查询结果的列名。

  • resultsCaseInsensitive 参数默认值为 true

如果希望使用大小写敏感的方式存储查询结果,需要将其设置为 false

JdbcTemplate jdbc = ...
jdbc.setResultsCaseInsensitive(false);

List<Map<String, Object>> result = jdbc.queryForList("select * from users");

通过设置 jdbc.setResultsCaseInsensitive(false); 可以决定结果集列名的大小写敏感性。

  • 默认情况下,属性值为 true 表示结果集列名是大小写不敏感。
  • 当设置为 false 时,会使用 LinkedHashMap 存放数据,结果集中列名会严格区分大小写。

结果处理器

  • ResultSetExtractor,使用 ResultSetExtractor 接口,自定义 ResultSet 结果集的处理。
  • RowMapper,使用 RowMapper 的好处是无需关心 ResultSet 的处理过程,只需要将编程工作专注在每一行数据的处理上。
  • RowCallbackHandler,通常可以用来流式的处理大规模数据。数据在处理过程中不会遗留在内存中。
  • TypeHandler,通过 @BindTypeHandler 注解关联一个 TypeHandler,可在一行一列或单列多行的查询中处理字段的序列化/反序列化。

查询参数说明

在查询相关 API 中,参数统一按 Object 传递,可支持的类型如下:

  • 位置参数(相当于 Object... args
    • 数组、Collection、TypeRegistry 中注册的类型
  • 名称化参数
    • Map、实体 Bean、SqlArg(名称会固定为 arg0
  • 自定义,完全自定义参数设置逻辑,适合需要手动类型控制的场景。
  • 动态 SQL
    • 使用 规则 构建查询命令时进行参数绑定,支持名称化参数。
    • 使用 ${...} 进行 SQL 注入 时虽然不会参与 PreparedStatement 但也算所传参的一种。

参数类型最终会经过 TypeHandler 处理,确保 JDBC 类型与 Java 类型的正确映射。