Wie kann man IN () SQL-Abfragen mit Spring's JDBCTemplate effektiv ausführen?

177

Ich habe mich gefragt, ob es eine elegantere Möglichkeit gibt, IN () - Abfragen mit Spring's JDBCTemplate durchzuführen. Momentan mache ich so etwas:

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

Das ist ziemlich schmerzhaft, da ich nur neun Zeilen habe, um die Klausel für die IN () - Abfrage zu erstellen. Ich hätte gerne so etwas wie die Parametersubstitution von vorbereiteten Anweisungen

Malax
quelle

Antworten:

275

Sie möchten eine Parameterquelle:

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

Dies funktioniert nur, wenn getJdbcTemplate()eine Instanz vom Typ zurückgegeben wirdNamedParameterJdbcTemplate

gähnen
quelle
5
Perfekt, das NamedParameterJdbcTemplate war genau das, wonach ich gesucht habe. Zusätzlich mag ich benannte Parameter mehr als diese Fragezeichen überall. Vielen Dank!
Malax
5
Dies funktioniert für kleine Listen, aber der Versuch, es für eine große Liste zu verwenden, führt zu einer Abfrage, bei der: ids durch "?,?,?,?,? ......" ersetzt wird und genügend Listenelemente überlaufen. Gibt es eine Lösung für große Listen?
nsayer
Sie sollten die Werte wahrscheinlich in eine temporäre Tabelle einfügen und die Bedingung mit erstellen WHERE NOT EXISTS (SELECT ...).
Gähnen
6
So schließen Sie die Antwort ab: Spring 3.1-Referenz - Übergeben von Wertelisten für die IN-Klausel . Aber in Referenz wurde nichts gesagt: Es ist möglich, jede Sammlung zu übergeben .
Timofey Gorshkov
9
seltsam, ich erhalte "Fehlercode [17004]; ungültiger Spaltentyp", wenn ich dies versuche.
Trevor
61

Ich mache die "in Klausel" Abfrage mit spring jdbc wie folgt:

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);
Herr Lou
quelle
10
Sie haben gerade eine Antwort auf eine fast drei Jahre alte Frage mit der gleichen Lösung wie die akzeptierte Antwort gepostet. Gibt es einen guten Grund dafür? :-)
Malax
16
Diese Antwort bietet mehr Klarheit, da sie zeigt, dass das NamedParameterJdbcTemplate für diese API benötigt wird. Vielen Dank für das zusätzliche Detail janwen
IcedDante
@ janwen, Danke für die Lösung !!! Es funktioniert gut gemäß meiner Anforderung !!
Karthik Amarnath Saakre
19

Wenn Sie eine Ausnahme erhalten für: Ungültiger Spaltentyp

Bitte verwenden Sie getNamedParameterJdbcTemplate()anstelle vongetJdbcTemplate()

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

Beachten Sie, dass die beiden zweiten Argumente vertauscht werden.

Mahmood Omari
quelle
2
Dies scheint keine Antwort auf diese Frage zu sein. Sollte es ein Kommentar zu einer anderen Antwort sein?
Dave Schweisguth
2
@ DaveSchweisguth Zwei Jahre später ist es definitiv eine Antwort wert.
Dwjohnston
2

Siehe hier

Abfrage mit benanntem Parameter schreiben, einfach ListPreparedStatementSettermit allen Parametern nacheinander verwenden. Fügen Sie einfach das folgende Snippet hinzu, um die Abfrage in traditioneller Form basierend auf den verfügbaren Parametern zu konvertieren.

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;
Abhishek Chatterjee
quelle
Für mich war dies die einzige Antwort, die funktionierte, da ich nur wenige Platzhalter setzen wollte
Kapil
-4

Viele Dinge haben sich seit 2009 geändert, aber ich kann nur Antworten finden, die besagen, dass Sie NamedParametersJDBCTemplate verwenden müssen.

Bei mir funktioniert es, wenn ich nur eine mache

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

Verwenden von SimpleJDBCTemplate oder JDBCTemplate

luso
quelle
11
Das Problem bei dieser Lösung besteht darin, dass der Inhalt listeParamsForInClausenicht maskiert wird und Sie für SQL-Injection anfällig sind.
Malax