java—使用jdbc参数化in子句的最佳方法是什么?

6qftjkof  于 2021-06-30  发布在  Java
关注(0)|答案(10)|浏览(373)

这个问题在这里已经有答案了

准备好的替代条款声明(30个答案)
两年前关门了。
假设我有一个查询表

SELECT * FROM MYTABLE WHERE MYCOL in (?)

我想参数化中的参数。
有没有一种简单的方法可以用jdbc在java中实现这一点,这种方法可以在不修改sql本身的情况下处理多个数据库?
我发现的最接近的问题与c有关,我想知道java/jdbc是否有不同之处。

s3fp2yjn

s3fp2yjn1#

我通过构造包含 ? 因为我要寻找价值观。

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

首先,我搜索了一个可以传递到语句中的数组类型,但是所有jdbc数组类型都是特定于供应商的。所以我就留下来了 ? .

nkhmeac6

nkhmeac62#

另外,jdbc中没有标准支持将集合作为参数处理。这将是伟大的,如果你能在一个名单中传递,这将是扩大。
spring的jdbc访问支持将集合作为参数传递。你可以看看这是如何做到的灵感编码这个安全。
请参见将集合自动扩展为jdbc参数
(本文首先讨论hibernate,然后讨论jdbc)

2w3rbyxf

2w3rbyxf3#

sormula使这一点变得简单(参见示例4):

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

rfbsl7qr4#

因为没有人回答一个大in子句(超过100个)的问题,所以我将抛出这个问题的解决方案,它对jdbc非常有效。简言之,我更换了 IN 用一个 INNER JOIN 在tmp桌上。
我所做的是创建一个我称之为batchids的表,根据rdbms,我可以创建一个tmp表或内存表。
该表有两列。一列具有in子句中的id,另一列具有我动态生成的批处理id。

SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?

在您选择之前,您将您的id推入具有给定批处理id的表中。然后您只需将原始查询in子句替换为ids表上的内部联接匹配,其中批处理id等于当前批处理。完成后,删除批处理的条目。

mnowg1ta

mnowg1ta5#

标准方法是(如果您使用的是SpringJDBC)使用org.springframework.jdbc.core.namedparam.namedparameterjdbctemplate类。
使用此类,可以将列表定义为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;
}
p5fdfcr1

p5fdfcr16#

在jdbc中确实没有直接的方法可以做到这一点。一些jdbc驱动程序似乎支持 PreparedStatement#setArray()IN 条款。我只是不知道是哪一个。
您可以使用一个helper方法 String#join() 以及 Collections#nCopies() 为生成占位符 IN 子句和另一个helper方法来设置循环中的所有值 PreparedStatement#setObject() .

public static String preparePlaceHolders(int length) {
    return String.join(",", Collections.nCopies(length, "?"));
}

public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
    for (int i = 0; i < values.length; i++) {
        preparedStatement.setObject(i + 1, values[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;
}

请注意,有些数据库在 IN 条款。例如,oracle对1000个项目有此限制。

ruarlubt

ruarlubt7#

我从docs.spring(19.7.3)那里得到了答案
sql标准允许基于包含变量值列表的表达式选择行。一个典型的例子是select*fromtu actor,其中id在(1,2,3)中。jdbc标准不直接支持这个变量列表;不能声明可变数量的占位符。您需要为所需数量的占位符准备许多变体,或者需要在知道需要多少占位符后动态生成sql字符串。namedparameterjdbctemplate和jdbctemplate中提供的命名参数支持采用后一种方法。作为基本对象的java.util.list传入值。此列表将用于插入所需的占位符,并在语句执行期间传递值。
希望这能对你有所帮助。

roqulrg3

roqulrg38#

看我的试用和它的成功,据说列表大小有潜在的限制。list l=arrays.aslist(新整数[]{12496124971249812499});map param=collections.singletonmap(“goodsid”,l);

NamedParameterJdbcTemplate  namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
    String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid in(:goodsid)";
    List<Long> list = namedParameterJdbcTemplate.queryForList(sql, param2, Long.class);
xqk2d5yq

xqk2d5yq9#

我们可以使用不同的替代方法。
执行单个查询-速度慢,不建议使用
使用存储过程-特定于数据库
动态创建preparedstatement查询-性能良好,但缓存的好处不明显,需要重新编译
在preparedstatement查询中使用null-我认为这是一种性能最佳的好方法。
请在此处查看有关这些的更多详细信息。

oknrviil

oknrviil10#

我能想到的一种方法是使用java.sql.preparedstatement和一些陪审团操纵
preparedstatement preparedstmt=conn.preparestatement(“select*from mytable where mycol in(?);
... 然后。。。
preparedstmt.setstring(1,[字符串参数]);
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html

相关问题