如何使用 Spring 的 JDBCTemplate 有效地执行 IN() SQL 查询?

Posted

技术标签:

【中文标题】如何使用 Spring 的 JDBCTemplate 有效地执行 IN() SQL 查询?【英文标题】:How to execute IN() SQL queries with Spring's JDBCTemplate effectively? 【发布时间】:2009-08-25 09:16:57 【问题描述】:

我想知道是否有更优雅的方式来使用 Spring 的 JDBCTemplate 进行 IN() 查询。目前我正在做这样的事情:

StringBuilder jobTypeInClauseBuilder = new StringBuilder();
for(int i = 0; i < jobTypes.length; i++) 
    Type jobType = jobTypes[i];

    if(i != 0) 
        jobTypeInClauseBuilder.append(',');
    

    jobTypeInClauseBuilder.append(jobType.convert());

这很痛苦,因为如果我有九行只是用于构建 IN() 查询的子句。我想要类似准备好的语句的参数替换

【问题讨论】:

【参考方案1】:

你想要一个参数来源:

Set<Integer> ids = ...;

MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("ids", ids);

List<Foo> foo = getJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",
     parameters, getRowMapper());

这仅适用于getJdbcTemplate() 返回NamedParameterJdbcTemplate 类型的实例

【讨论】:

完美,NamedParameterJdbcTemplate 正是我想要的。此外,我更喜欢命名参数,而不是到处都是问号。非常感谢! 这适用于小列表,但尝试在大列表上使用它会导致查询中 :ids 被替换为 "?,?,?,?,?......"并且有足够的列表项它会溢出。是否有适用于大型列表的解决方案? 您可能应该将值插入临时表并使用WHERE NOT EXISTS (SELECT ...) 构建条件。 完整回答:Spring 3.1 Reference — Passing in lists of values for IN clause。但在 Reference 中什么也没说:it is possible to pass any Collection. 奇怪,当我尝试这个时,我得到“错误代码 [17004];无效的列类型”。【参考方案2】:

我使用 spring jdbc 执行“in 子句”查询,如下所示:

String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid IN (:goodsid)";

List ids = Arrays.asList(new Integer[]12496,12497,12498,12499);
Map<String, List> paramMap = Collections.singletonMap("goodsid", ids);
NamedParameterJdbcTemplate template = 
    new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());

List<Long> list = template.queryForList(sql, paramMap, Long.class);

【讨论】:

您刚刚发布了一个已存在近三年的问题的答案,其解决方案与已接受的答案相同。这背后有什么好的理由吗? :-) 这个答案提供了更多的清晰度,因为它说明了这个 API 需要 NamedParameterJdbcTemplate ...所以感谢 janwen 提供的额外细节 @janwen ,感谢您的解决方案!!!根据我的要求,它工作正常!!【参考方案3】:

如果您遇到异常:无效的列类型

请使用getNamedParameterJdbcTemplate() 而不是getJdbcTemplate()

 List<Foo> foo = getNamedParameterJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",parameters,
 getRowMapper());

请注意,后两个参数是交换的。

【讨论】:

这似乎不是这个问题的答案。它应该是对另一个答案的评论吗? @DaveSchweisguth 两年后,它绝对值得回答。 getNamedParameterJdbcTemplate() 未定义。如果需要该方法,需要扩展 NamedParameterJdbcDaoSupport。【参考方案4】:

参考here

使用命名参数编写查询,按顺序使用简单的ListPreparedStatementSetter 和所有参数。只需在sn-p下方添加,即可将传统形式的查询转换为可用参数,

ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(namedSql);

List<Integer> parameters = new ArrayList<Integer>();
for (A a : paramBeans)
    parameters.add(a.getId());

MapSqlParameterSource parameterSource = new MapSqlParameterSource();
parameterSource.addValue("placeholder1", parameters);
// create SQL with ?'s
String sql = NamedParameterUtils.substituteNamedParameters(parsedSql, parameterSource);     
return sql;

【讨论】:

对我来说这是唯一可行的答案,因为我只想设置几个占位符【参考方案5】:

自 2009 年以来发生了很多变化,但我只能找到需要使用 NamedParametersJDBCTemplate 的答案。

对我来说,只要我做一个就可以了

db.query(sql, new MyRowMapper(), StringUtils.join(listeParamsForInClause, ","));

使用 SimpleJDBCTemplate 或 JDBCTemplate

【讨论】:

这个解决方案的问题是,listeParamsForInClause 中的内容不会被转义,让你容易受到 SQL 注入的攻击。

以上是关于如何使用 Spring 的 JDBCTemplate 有效地执行 IN() SQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章

spring5:JdbcTemplate

Spring_11-Spring5总结

阶段3 2.Spring_10.Spring中事务控制_7 spring基于注解的声明式事务控制

放弃 MyBatisJPA,我最终选择了 JDBC Template…

请简述啥是CSS3 Spring?如何使用Spring

spring中如何使用策略模式