Làm cách nào để thực thi các truy vấn SQL IN () với JDBCTemplate của Spring một cách hiệu quả?


177

Tôi đã tự hỏi nếu có một cách thanh lịch hơn để thực hiện các truy vấn IN () với JDBCTemplate của Spring. Hiện tại tôi làm một cái gì đó như thế:

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());
}

Điều này khá đau đớn vì nếu tôi có chín dòng chỉ để xây dựng mệnh đề cho truy vấn IN (). Tôi muốn có một cái gì đó giống như sự thay thế tham số của các câu lệnh đã chuẩn bị

Câu trả lời:


275

Bạn muốn có một nguồn tham số:

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());

Điều này chỉ hoạt động nếu getJdbcTemplate()trả về một thể hiện của loạiNamedParameterJdbcTemplate


5
Hoàn hảo, NamedParameterJdbcTemplate chính xác là những gì tôi đang tìm kiếm. Ngoài ra, tôi thích các tham số được đặt tên nhiều hơn các dấu hỏi ở khắp mọi nơi. Cảm ơn rất nhiều!
Malax

5
Điều này hoạt động cho các danh sách nhỏ, nhưng cố gắng sử dụng nó trong danh sách lớn sẽ dẫn đến một truy vấn trong đó: id được thay thế bằng "?,?,?,?,? ......" và với đủ các mục danh sách mà nó tràn ra. Có một giải pháp làm việc cho danh sách lớn?
nsayer

Bạn có thể nên chèn các giá trị vào một bảng tạm thời và xây dựng điều kiện bằng cách sử dụng WHERE NOT EXISTS (SELECT ...).
ngáp

6
Để trả lời đầy đủ: Tham chiếu Spring 3.1 - Chuyển qua danh sách các giá trị cho mệnh đề IN . Nhưng trong Tài liệu tham khảo không có gì nói về: có thể vượt qua bất kỳ Bộ sưu tập nào .
Timofey Gorshkov

9
lạ, tôi nhận được "mã lỗi [17004]; Loại cột không hợp lệ" khi tôi thử điều này.
Trevor

61

Tôi thực hiện truy vấn "trong mệnh đề" với spring jdbc như thế này:

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);

10
Bạn vừa đăng một câu trả lời cho một câu hỏi gần ba năm tuổi với cùng một giải pháp như câu trả lời được chấp nhận. Có bất kỳ lý do tốt đằng sau này? :-)
Malax

16
Câu trả lời này cung cấp rõ ràng hơn vì nó minh họa rằng NamedParameterJdbcTemplate là cần thiết cho API này ... vì vậy cảm ơn vì chi tiết bổ sung janwen
IcedDante 17/03/2015

@janwen, Cảm ơn giải pháp !!! Nó hoạt động tốt theo yêu cầu của tôi !!
Karthik Amarnath Saakre

19

Nếu bạn nhận được một ngoại lệ cho: Loại cột không hợp lệ

Vui lòng sử dụng getNamedParameterJdbcTemplate()thay vìgetJdbcTemplate()

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

Lưu ý rằng hai đối số thứ hai được hoán đổi xung quanh.


2
Đây dường như không phải là một câu trả lời cho câu hỏi này. Nó có nên là một nhận xét về câu trả lời khác?
Dave Schweisguth

2
@DaveSchweisguth Hai năm sau, nó chắc chắn đảm bảo là một câu trả lời.
dwjohnston

2

Tham khảo tại đây

viết truy vấn với tham số được đặt tên, sử dụng đơn giản ListPreparedStatementSettervới tất cả các tham số theo thứ tự. Chỉ cần thêm đoạn mã bên dưới để chuyển đổi truy vấn ở dạng truyền thống dựa trên các tham số có sẵn,

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;

Đối với tôi đây là câu trả lời duy nhất hoạt động vì tôi chỉ muốn đặt vài chỗ giữ chỗ
Kapil

-4

Nhiều thứ đã thay đổi kể từ năm 2009, nhưng tôi chỉ có thể tìm thấy câu trả lời nói rằng bạn cần sử dụng NamedParameterJDBCTemplate.

Đối với tôi nó hoạt động nếu tôi chỉ làm một

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

sử dụng SimpleJDBCTemplate hoặc JDBCTemplate


11
Vấn đề với giải pháp này là, nội dung trong listeParamsForInClausesẽ không được thoát ra và khiến bạn dễ bị tiêm SQL.
Malax
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.