Java 使用JDBC参数化IN子句的最佳方法是什么?

Java 使用JDBC参数化IN子句的最佳方法是什么?,java,sql,jdbc,in-clause,Java,Sql,Jdbc,In Clause,假设我对表单有一个查询 SELECT * FROM MYTABLE WHERE MYCOL in (?) 我想参数化中的参数 在Java和JDBC中有没有一种简单的方法可以做到这一点,这种方法可以在不修改SQL本身的情况下处理多个数据库 最近,我想知道Java/JDBC是否有不同之处 我通过构造包含尽可能多的?值的SQL字符串来解决这个问题 SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?) 首先,我搜索了一个可以传递到语句中的数组类型,但所有JDB

假设我对表单有一个查询

SELECT * FROM MYTABLE WHERE MYCOL in (?)
我想参数化中的参数

在Java和JDBC中有没有一种简单的方法可以做到这一点,这种方法可以在不修改SQL本身的情况下处理多个数据库


最近,我想知道Java/JDBC是否有不同之处

我通过构造包含尽可能多的
值的SQL字符串来解决这个问题

SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)

首先,我搜索了一个可以传递到语句中的数组类型,但所有JDBC数组类型都是特定于供应商的。因此,我继续使用多个

我能想到的一种方法是使用java.sql.PreparedStatement和一些陪审团操纵

PreparedStatement preparedStmt=conn.prepareStatement(“从MYTABLE中选择*,其中MYCOL位于(?)

。。。然后

preparedStmt.setString(1,[您的字符串参数])


AFAIK,JDBC中没有标准支持将集合作为参数处理。如果你能通过一个列表,这将是一个伟大的扩展

Spring的JDBC访问支持将集合作为参数传递。您可以看看这是如何做到的,以获得安全编码的灵感


(本文首先讨论Hibernate,然后讨论JDBC。)

在JDBC中确实没有直接的方法来实现这一点。一些JDBC驱动程序似乎支持子句中的
。我只是不确定是哪一个

您可以使用带有and的helper方法为
子句中的
生成占位符,并使用另一个helper方法设置循环中的所有值

公共静态字符串preparePlaceHolders(int-length){
返回字符串.join(“,”,Collections.nCopies(长度“?”);
}
公共静态void setValues(PreparedStatement PreparedStatement,Object…values)抛出SQLException{
对于(int i=0;i
以下是您可以如何使用它:

private static final String SQL_FIND = "SELECT id, name, value FROM entity WHERE id IN (%s)";

public List<Entity> find(Set<Long> ids) throws SQLException {
    List<Entity> entities = new ArrayList<Entity>();
    String sql = String.format(SQL_FIND, preparePlaceHolders(ids.size()));

    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(sql);
    ) {
        setValues(statement, ids.toArray());

        try (ResultSet resultSet = statement.executeQuery()) {
            while (resultSet.next()) {
                entities.add(map(resultSet));
            }
        }
    }

    return entities;
}

private static Entity map(ResultSet resultSet) throws SQLException {
    Enitity entity = new Entity();
    entity.setId(resultSet.getLong("id"));
    entity.setName(resultSet.getString("name"));
    entity.setValue(resultSet.getInt("value"));
    return entity;
}
private static final String SQL\u FIND=“从id位于(%s)中的实体中选择id、名称、值”;
公共列表查找(设置ID)引发SQLException{
列表实体=新的ArrayList();
String sql=String.format(sql_FIND,preparePlaceHolders(ids.size());
试一试(
Connection=dataSource.getConnection();
PreparedStatement=connection.prepareStatement(sql);
) {
setValues(语句,ids.toArray());
try(ResultSet ResultSet=statement.executeQuery()){
while(resultSet.next()){
添加(映射(结果集));
}
}
}
返回实体;
}
私有静态实体映射(ResultSet ResultSet)引发SQLException{
Enitity实体=新实体();
entity.setId(resultSet.getLong(“id”);
entity.setName(resultSet.getString(“name”);
entity.setValue(resultSet.getInt(“value”);
返回实体;
}

请注意,某些数据库在
子句中的
中有允许的值数量限制。例如,Oracle对1000个项目有此限制。

参见我的试用版和It成功版,据说列表大小有潜在限制。 List l=Arrays.asList(新整数[]{12496124971249812499}); Map param=Collections.singletonMap(“goodsid”,l)

NamedParameterJdbcTemplate NamedParameterJdbcTemplate=newnamedParameterJDBCTemplate(getJdbcTemplate().getDataSource());
String sql=“从beiker_goods bg中选择bg.goodsid,其中bg.goodsid位于(:goodsid)”;
List List=namedParameterJdbcTemplate.queryForList(sql,param2,Long.class);
sormula使这一点变得简单(请参阅):

ArrayList partNumbers=new ArrayList();
零件号。添加(999);
零件号。添加(777);
零件号。添加(1234);
//设立
数据库=新数据库(getConnection());
Table inventoryTable=database.getTable(Inventory.class);
//选择列表“…中的零件号(?,?)…”的操作
对于(库存:inventoryTable)。
选择Allwhere(“零件号”,零件号))
{
System.out.println(inventory.getPartNumber());
}

由于没有人回答大型IN子句(超过100个)的问题我将给出这个问题的解决方案,它对JDBC非常有效。简言之,我将
中的
替换为tmp表上的
内部联接

我所做的是创建一个我称之为批处理ID表的表,根据RDBMS的不同,我可以创建一个tmp表或内存表

该表有两列。一列具有IN子句中的id,另一列具有我动态生成的批处理id

SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
在选择之前,将id放入具有给定批次id的表中。
然后,您只需在ids表中将原始查询IN子句替换为内部联接匹配,其中batch_id等于当前批次。完成批处理后,删除条目。

执行此操作的标准方法是(如果使用Spring JDBC)使用类

使用此类,可以将列表定义为SQL参数,并使用NamedParameterJdbcTemplate替换命名参数。例如:

public List<MyObject> getDatabaseObjects(List<String> params) {
    NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    String sql = "select * from my_table where my_col in (:params)";
    List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper);
    return result;
}
公共列表getDatabaseObjects(列表参数){
NamedParameterJdbcTemplate jdbcTemplate=新的NamedParameterJdbcTemplate(数据源);
String sql=“从我的列所在的我的表中选择*(:params)”;
List result=jdbcTemplate.query(sql,Collections.singletonMap(“params”,params),myRowMapper);
返回结果;
}

我们可以使用不同的替代方法

  • 执行单个查询-速度慢,不推荐使用
  • 使用存储过程-特定于数据库
  • 动态创建PreparedStatement查询-性能良好,但缓存带来的好处不多,需要重新编译
  • 在PreparedStatement查询中使用NULL-我认为这是一种具有最佳性能的好方法
    ArrayList<Integer> partNumbers = new ArrayList<Integer>();
    partNumbers.add(999);
    partNumbers.add(777);
    partNumbers.add(1234);
    
    // set up
    Database database = new Database(getConnection());
    Table<Inventory> inventoryTable = database.getTable(Inventory.class);
    
    // select operation for list "...WHERE PARTNUMBER IN (?, ?, ?)..."
    for (Inventory inventory: inventoryTable.
        selectAllWhere("partNumberIn", partNumbers))    
    {
        System.out.println(inventory.getPartNumber());
    }
    
    SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
    
    public List<MyObject> getDatabaseObjects(List<String> params) {
        NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
        String sql = "select * from my_table where my_col in (:params)";
        List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper);
        return result;
    }