我们将学习如何将List传递到 Spring JDBC 模板查询的 IN 子句中。
SELECT * FROM EMPLOYEE WHERE id IN (1, 2, 3)
IN子句里是固定长度的不在今天的讨论范围。通常情况下IN子句里的值数量是可变的,因此我们需要创建一个可以支持动态List的占位符。
- List
getEmployeesFromIdList(List ids) { - String inSql = String.join(",", Collections.nCopies(ids.size(), "?"));
-
- List
employees = jdbcTemplate.query( - String.format("SELECT * FROM EMPLOYEE WHERE id IN (%s)", inSql),
- ids.toArray(),
- (rs, rowNum) -> new Employee(rs.getInt("id"), rs.getString("first_name"),
- rs.getString("last_name")));
-
- return employees;
- }
首先我们先生成一个List长度的用逗号分隔的“?”占位符字符串,然后拼接到IN子句里。比如我们ids的List的长度为3,生成的SQL声明如下:
SELECT * FROM EMPLOYEE WHERE id IN (?,?,?)
我们用ids List做为参数。通过这种方式,我们能够基于输入的List执行动态SQL。
另一种执行不固定长度的List做为IN子句条件的方法是用 NamedParameterJdbcTemplate 。
如:
- List
getEmployeesFromIdListNamed(List ids) { - SqlParameterSource parameters = new MapSqlParameterSource("ids", ids);
-
- List
employees = namedJdbcTemplate.query( - "SELECT * FROM EMPLOYEE WHERE id IN (:ids)",
- parameters,
- (rs, rowNum) -> new Employee(rs.getInt("id"), rs.getString("first_name"),
- rs.getString("last_name")));
-
- return employees;
- }
当List中有大量值时,我们应该考虑将List传递给 JdbcTemplate 查询的替代方法。
例如,IN语句中可放的最大参数个数是1000个。mysql中,IN语句中参数个数是不限制的。不过对整段sql语句的长度有了限制(max_allowed_packet)。
遇到这种情况 ,可以为List创建一个临时表。不过需要注意的是,不同数据库有不同的方法来创建临时表。比如,我们能用“CREATE GLOBAL TEMPORARY TABLE ”语句在Oracle数据库里创建临时表。
- List
getEmployeesFromLargeIdList(List ids) { - jdbcTemplate.execute("CREATE TEMPORARY TABLE IF NOT EXISTS employee_tmp (id INT NOT NULL)");
-
- List
- for (Integer id : ids) {
- employeeIds.add(new Object[] { id });
- }
- jdbcTemplate.batchUpdate("INSERT INTO employee_tmp VALUES(?)", employeeIds);
-
- List
employees = jdbcTemplate.query( - "SELECT * FROM EMPLOYEE WHERE id IN (SELECT id FROM employee_tmp)",
- (rs, rowNum) -> new Employee(rs.getInt("id"), rs.getString("first_name"),
- rs.getString("last_name")));
-
- jdbcTemplate.update("DELETE FROM employee_tmp");
-
- return employees;
- }
我们首先创建一个临时表,然后将List值insert到临时表。
在IN语句里的值从临时表里查询获取,通过这种方式我们可以避免IN语句里有太多参数。
最后我们完成查询后,删除掉临时表以便以后在用。