使用 JdbcTemplate 插入多行
Posted
技术标签:
【中文标题】使用 JdbcTemplate 插入多行【英文标题】:Inserting multiple rows using JdbcTemplate 【发布时间】:2011-03-11 02:13:51 【问题描述】:如何使用在 mysql 上运行的 JdbcTemplate 以可扩展的方式执行以下 SQL。在这种情况下,可扩展意味着:
-
服务器上只执行一条 SQL 语句
它适用于任意数量的行。
声明如下:
INSERT INTO myTable (foo, bar) VALUES ("asdf", "asdf"), ("qwer", "qwer")
假设我有一个带有 foo
和 bar
字段的 POJO 列表。我意识到我可以遍历列表并执行:
jdbcTemplate.update("INSERT INTO myTable(foo, bar) VALUES (?, ?)", paramMap)
但这并没有满足第一个标准。
我相信我也可以执行:
jdbcTemplate.batchUpdate("INSERT INTO myTable(foo, bar) VALUES (?, ?)", paramMapArray)
但据我所知,这只会编译 SQL 一次并执行多次,再次失败第一个标准。
似乎同时满足这两个标准的最后一种可能性是自己使用StringBuffer
简单地构建 SQL,但我想避免这种情况。
【问题讨论】:
我们可以只使用 JDBC 做同样的事情吗?? 这与 JdbcTemplate 甚至 JDBC 无关。你不能在 SQL、句号(或标准 SQL,无论如何)中做到这一点,所以你当然不能在 JdbcTemplate 中做到。 @skaffman:我更新了我的问题,说我正在使用 mySQL。也许这是一个 mySQL-only 功能,但它在dev.mysql.com/doc/refman/5.1/en/insert.html 中描述了大约四分之一:“使用 VALUES 语法的 INSERT 语句可以插入多行。为此,请包含多个列值列表,每个列表都包含在括号中并用逗号分隔。示例:“ @Teja:是的,可以在纯 JDBC 中完成,但这不是问题所在。我已经用第三种可能性更新了这个问题,那就是手动构建 SQL。 如果您使用 InnoDB,那么 batchUpdate 应该 仅在最后一次插入后更新索引表。使用单个语句获得的唯一效率提升是您必须向 MySQL 服务器发送更少的数据。我怀疑您是否能够使用标准 JdbcTemplate 进行多次插入,但您始终可以扩展 JdbcTemplate 并滚动您自己的批量插入方法,该方法手动构建插入字符串。 【参考方案1】:您可以像下面这样使用 BatchPreparedStatementSetter。
public void insertListOfPojos(final List<MyPojo> myPojoList)
String sql = "INSERT INTO "
+ "MY_TABLE "
+ "(FIELD_1,FIELD_2,FIELD_3) "
+ "VALUES " + "(?,?,?)";
getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter()
@Override
public void setValues(PreparedStatement ps, int i)
throws SQLException
MyPojo myPojo = myPojoList.get(i);
ps.setString(1, myPojo.getField1());
ps.setString(2, myPojo.getField2());
ps.setString(3, myPojo.getField3());
@Override
public int getBatchSize()
return myPojoList.size();
);
【讨论】:
在使用 :NamedParameters 而不是 ? 时出现“参数索引超出范围(1 > 参数数量,即 0)”。与 NamedParameterJdbcTemplate 一起使用的一些更新?【参考方案2】:多行插入(使用“行值构造函数”)实际上是 SQL-92 标准的一部分。看 http://en.wikipedia.org/wiki/Insert_(SQL)#Multirow_inserts.
有些数据库不支持这种语法,但很多支持。根据我的经验,Derby/Cloudscape、DB2、Postgresql 和较新的 Hypersonic 2.*+ 版本确实支持这一点。
您对让它作为 PreparedStatement 工作的担忧是可以理解的,但我看到类似的情况,Spring JDBC 确实为某些查询自动处理项目集合(比如 where in (?)),但我不能保证这种情况。
我确实找到了一些可能有用的信息(无法在这篇文章中添加第二个链接) 这可能会有所帮助。
我可以告诉您,从最严格的意义上说,您的第二个要求(适用于任意数量的参数)可能无法满足:我使用的每个数据库都会施加查询长度限制,这将发挥作用。
【讨论】:
不允许我在上面发布的那个 URL 是:fusesource.com/docs/router/2.2/transactions/…【参考方案3】:在我看来 JdbcTemplate 的 batchUpdate() 方法在这种情况下可能会有所帮助(从这里复制 http://www.mkyong.com/spring/spring-jdbctemplate-batchupdate-example/):
//insert batch example
public void insertBatch(final List<Customer> customers)
String sql = "INSERT INTO CUSTOMER " +
"(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter()
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException
Customer customer = customers.get(i);
ps.setLong(1, customer.getCustId());
ps.setString(2, customer.getName());
ps.setInt(3, customer.getAge() );
@Override
public int getBatchSize()
return customers.size();
);
【讨论】:
非常感谢,这正是我要找的!【参考方案4】:你也可以试试 jdbcInsert.executeBatch(sqlParamSourceArray)
// define parameters
jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("TABlE_NAME");
SqlParameterSource[] sqlParamSourceArray = new SqlParameterSource[apiConsumer
.getApiRoleIds().size()];
for (int i = 0; i < myCollection.size(); i++)
sqlParamSourceArray[i] = new MapSqlParameterSource().addValue("COL1");
......................
// execute insert
int[] keys = jdbcInsert.executeBatch(sqlParamSourceArray);
【讨论】:
这个答案是错误的!jdbcInsert.executeBatch()
不返回密钥。它返回the array of number of rows affected as returned by the JDBC driver
。请参阅 javadocs。 docs.spring.io/spring-framework/docs/current/javadoc-api/org/…【参考方案5】:
您不能在 JDBC 中执行此操作,期间。在 MySQL 中它只是语法糖,但该语句的效果将与发出多个 INSERT 语句相同。所以你可以使用batchUpdate,它会产生同样的效果。
【讨论】:
不正确。 MySQL 的“扩展插入”(如问题中所述)比批量插入(您提前准备但一次插入一行)更快。它不是 MySQL 中的语法糖。以上是关于使用 JdbcTemplate 插入多行的主要内容,如果未能解决你的问题,请参考以下文章
使用 jdbcTemplate.batchUpdate 混淆批量插入