PreparedStatement IN clause alternatives?

前端 未结 30 3893
情歌与酒
情歌与酒 2020-11-21 05:19

What are the best workarounds for using a SQL IN clause with instances of java.sql.PreparedStatement, which is not supported for multiple values du

相关标签:
30条回答
  • 2020-11-21 05:44

    No simple way AFAIK. If the target is to keep statement cache ratio high (i.e to not create a statement per every parameter count), you may do the following:

    1. create a statement with a few (e.g. 10) parameters:

      ... WHERE A IN (?,?,?,?,?,?,?,?,?,?) ...

    2. Bind all actuall parameters

      setString(1,"foo"); setString(2,"bar");

    3. Bind the rest as NULL

      setNull(3,Types.VARCHAR) ... setNull(10,Types.VARCHAR)

    NULL never matches anything, so it gets optimized out by the SQL plan builder.

    The logic is easy to automate when you pass a List into a DAO function:

    while( i < param.size() ) {
      ps.setString(i+1,param.get(i));
      i++;
    }
    
    while( i < MAX_PARAMS ) {
      ps.setNull(i+1,Types.VARCHAR);
      i++;
    }
    
    0 讨论(0)
  • 2020-11-21 05:46

    I suppose you could (using basic string manipulation) generate the query string in the PreparedStatement to have a number of ?'s matching the number of items in your list.

    Of course if you're doing that you're just a step away from generating a giant chained OR in your query, but without having the right number of ? in the query string, I don't see how else you can work around this.

    0 讨论(0)
  • 2020-11-21 05:47

    I came across a number of limitations related to prepared statement:

    1. The prepared statements are cached only inside the same session (Postgres), so it will really work only with connection pooling
    2. A lot of different prepared statements as proposed by @BalusC may cause the cache to overfill and previously cached statements will be dropped
    3. The query has to be optimized and use indices. Sounds obvious, however e.g. the ANY(ARRAY...) statement proposed by @Boris in one of the top answers cannot use indices and query will be slow despite caching
    4. The prepared statement caches the query plan as well and the actual values of any parameters specified in the statement are unavailable.

    Among the proposed solutions I would choose the one that doesn't decrease the query performance and makes the less number of queries. This will be the #4 (batching few queries) from the @Don link or specifying NULL values for unneeded '?' marks as proposed by @Vladimir Dyuzhev

    0 讨论(0)
  • 2020-11-21 05:48

    Spring allows passing java.util.Lists to NamedParameterJdbcTemplate , which automates the generation of (?, ?, ?, ..., ?), as appropriate for the number of arguments.

    For Oracle, this blog posting discusses the use of oracle.sql.ARRAY (Connection.createArrayOf doesn't work with Oracle). For this you have to modify your SQL statement:

    SELECT my_column FROM my_table where search_column IN (select COLUMN_VALUE from table(?))
    

    The oracle table function transforms the passed array into a table like value usable in the IN statement.

    0 讨论(0)
  • 2020-11-21 05:49

    PreparedStatement doesn't provide any good way to deal with SQL IN clause. Per http://www.javaranch.com/journal/200510/Journal200510.jsp#a2 "You can't substitute things that are meant to become part of the SQL statement. This is necessary because if the SQL itself can change, the driver can't precompile the statement. It also has the nice side effect of preventing SQL injection attacks." I ended up using following approach:

    String query = "SELECT my_column FROM my_table where search_column IN ($searchColumns)";
    query = query.replace("$searchColumns", "'A', 'B', 'C'");
    Statement stmt = connection.createStatement();
    boolean hasResults = stmt.execute(query);
    do {
        if (hasResults)
            return stmt.getResultSet();
    
        hasResults = stmt.getMoreResults();
    
    } while (hasResults || stmt.getUpdateCount() != -1);
    
    0 讨论(0)
  • 2020-11-21 05:49

    This worked for me (psuedocode):

    public class SqlHelper
    {
        public static final ArrayList<String>platformList = new ArrayList<>(Arrays.asList("iOS","Android","Windows","Mac"));
    
        public static final String testQuery = "select * from devices where platform_nm in (:PLATFORM_NAME)";
    }
    

    specicify binding :

    public class Test extends NamedParameterJdbcDaoSupport
    public List<SampleModelClass> runQuery()
    {
        //define rowMapper to insert in object of SampleClass
        final Map<String,Object> map = new HashMap<>();
        map.put("PLATFORM_LIST",DeviceDataSyncQueryConstants.platformList);
        return getNamedParameterJdbcTemplate().query(SqlHelper.testQuery, map, rowMapper)
    }
    
    0 讨论(0)
提交回复
热议问题