Java:使用 PreparedStatement 将多行插入 MySQL
Posted
技术标签:
【中文标题】Java:使用 PreparedStatement 将多行插入 MySQL【英文标题】:Java: Insert multiple rows into MySQL with PreparedStatement 【发布时间】:2011-05-20 07:05:42 【问题描述】:我想使用 Java 一次将多行插入到 mysql 表中。行数是动态的。过去我在做...
for (String element : array)
myStatement.setString(1, element[0]);
myStatement.setString(2, element[1]);
myStatement.executeUpdate();
我想优化它以使用 MySQL 支持的语法:
INSERT INTO table (col1, col2) VALUES ('val1', 'val2'), ('val1', 'val2')[, ...]
但是对于PreparedStatement
,我不知道有什么方法可以做到这一点,因为我事先不知道array
将包含多少元素。如果PreparedStatement
不可能,我还能怎么做(并且仍然转义数组中的值)?
【问题讨论】:
【参考方案1】:可以在JDBC
提交多个更新。
我们可以使用 Statement
、PreparedStatement
和 CallableStatement
对象进行批量更新,并禁用自动提交。
addBatch()
和 executeBatch()
函数可用于所有语句对象以进行 BatchUpdate。
这里addBatch()
方法将一组语句或参数添加到当前批处理中。
【讨论】:
【参考方案2】:这可能有助于您将数组传递给PreparedStatement
。
将所需的值存储到数组中并将其传递给函数以插入相同的值。
String sql= "INSERT INTO table (col1,col2) VALUES (?,?)";
String array[][] = new String [10][2];
for(int i=0;i<array.size();i++)
//Assigning the values in individual rows.
array[i][0] = "sampleData1";
array[i][1] = "sampleData2";
try
DBConnectionPrepared dbcp = new DBConnectionPrepared();
if(dbcp.putBatchData(sqlSaveAlias,array)==1)
System.out.println("Success");
else
System.out.println("Failed");
catch(Exception e)
e.printStackTrace();
putBatchData(sql,2D_Array)
public int[] putBatchData(String sql,String args[][])
int status[];
try
PreparedStatement stmt=con.prepareStatement(sql);
for(int i=0;i<args.length;i++)
for(int j=0;j<args[i].length;j++)
stmt.setString(j+1, args[i][j]);
stmt.addBatch();
stmt.executeBatch();
stmt.clearParameters();
status= stmt.executeBatch();
catch (Exception e)
e.printStackTrace();
return status;
【讨论】:
【参考方案3】:如果您可以动态创建 sql 语句,则可以执行以下解决方法:
String myArray[][] = "1-1", "1-2" , "2-1", "2-2" , "3-1", "3-2" ;
StringBuffer mySql = new StringBuffer("insert into MyTable (col1, col2) values (?, ?)");
for (int i = 0; i < myArray.length - 1; i++)
mySql.append(", (?, ?)");
myStatement = myConnection.prepareStatement(mySql.toString());
for (int i = 0; i < myArray.length; i++)
myStatement.setString(i, myArray[i][1]);
myStatement.setString(i, myArray[i][2]);
myStatement.executeUpdate();
【讨论】:
我相信接受的答案要好得多!!我不知道批量更新,当我开始写这个答案时,答案还没有提交!!! :) 这种方法比接受的方法快得多。我测试了它,但找不到原因。 @JohnS 你知道为什么吗? @julian0zzx 不,但可能是因为它作为单个 sql 而不是多个执行。但我不确定。【参考方案4】:使用 MySQL 驱动程序时,您必须将连接参数 rewriteBatchedStatements
设置为 true ( jdbc:mysql://localhost:3306/TestDB?**rewriteBatchedStatements=true**)
。
使用此参数,当表仅锁定一次且索引仅更新一次时,语句将重写为批量插入。所以速度要快得多。
没有这个参数唯一的好处是源代码更干净。
【讨论】:
这是对施工性能的评论: statement.addBatch(); if ((i + 1) % 1000 == 0) statement.executeBatch(); // 每 1000 个项目执行一次。 显然 MySQL 驱动程序有一个错误 bugs.mysql.com/bug.php?id=71528 这也会导致像 Hibernate 这样的 ORM 框架出现问题 hibernate.atlassian.net/browse/HHH-9134 是的。现在这也是正确的。至少对于5.1.45
mysql 连接器版本。
mysql-connector-javarewriteBatchedStatements=true
就没有性能提升。【参考方案5】:
@Ali Shakiba 您的代码需要一些修改。错误部分:
for (int i = 0; i < myArray.length; i++)
myStatement.setString(i, myArray[i][1]);
myStatement.setString(i, myArray[i][2]);
更新代码:
String myArray[][] =
"1-1", "1-2",
"2-1", "2-2",
"3-1", "3-2"
;
StringBuffer mySql = new StringBuffer("insert into MyTable (col1, col2) values (?, ?)");
for (int i = 0; i < myArray.length - 1; i++)
mySql.append(", (?, ?)");
mysql.append(";"); //also add the terminator at the end of sql statement
myStatement = myConnection.prepareStatement(mySql.toString());
for (int i = 0; i < myArray.length; i++)
myStatement.setString((2 * i) + 1, myArray[i][1]);
myStatement.setString((2 * i) + 2, myArray[i][2]);
myStatement.executeUpdate();
【讨论】:
这是整个答案中更快更好的方法。这应该是公认的答案 如已接受的答案中所述,某些 JDBC 驱动程序/数据库对您可以在 INSERT 语句中包含的行数有限制。在上述示例的情况下,如果myArray
的长度大于该限制,您将遇到异常。就我而言,我有一个 1,000 行的限制,这引起了批处理执行的需要,因为我可能在任何给定的运行中更新超过 1,000 行。如果您知道插入的内容少于允许的最大值,这种类型的语句理论上应该可以正常工作。需要记住的一点。
澄清一下,上面的答案提到了 JDBC 驱动程序/数据库对批处理长度的限制,但正如我在我的案例中看到的那样,插入语句中包含的行数也可能受到限制。
【参考方案6】:
如果您在表中有自动增量并需要访问它..您可以使用以下方法...在使用前进行测试,因为语句中的 getGeneratedKeys() 因为它取决于使用的驱动程序。以下代码在 Maria DB 10.0.12 和 Maria JDBC 驱动程序 1.2 上测试
请记住,增加批量大小只能在一定程度上提高性能...对于我的设置,将批量大小增加到 500 以上实际上会降低性能。
public Connection getConnection(boolean autoCommit) throws SQLException
Connection conn = dataSource.getConnection();
conn.setAutoCommit(autoCommit);
return conn;
private void testBatchInsert(int count, int maxBatchSize)
String querySql = "insert into batch_test(keyword) values(?)";
try
Connection connection = getConnection(false);
PreparedStatement pstmt = null;
ResultSet rs = null;
boolean success = true;
int[] executeResult = null;
try
pstmt = connection.prepareStatement(querySql, Statement.RETURN_GENERATED_KEYS);
for (int i = 0; i < count; i++)
pstmt.setString(1, UUID.randomUUID().toString());
pstmt.addBatch();
if ((i + 1) % maxBatchSize == 0 || (i + 1) == count)
executeResult = pstmt.executeBatch();
ResultSet ids = pstmt.getGeneratedKeys();
for (int i = 0; i < executeResult.length; i++)
ids.next();
if (executeResult[i] == 1)
System.out.println("Execute Result: " + i + ", Update Count: " + executeResult[i] + ", id: "
+ ids.getLong(1));
catch (Exception e)
e.printStackTrace();
success = false;
finally
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (connection != null)
if (success)
connection.commit();
else
connection.rollback();
connection.close();
catch (SQLException e)
e.printStackTrace();
【讨论】:
【参考方案7】:您可以通过PreparedStatement#addBatch()
创建批处理并通过PreparedStatement#executeBatch()
执行。
这是一个启动示例:
public void save(List<Entity> entities) throws SQLException
try (
Connection connection = database.getConnection();
PreparedStatement statement = connection.prepareStatement(SQL_INSERT);
)
int i = 0;
for (Entity entity : entities)
statement.setString(1, entity.getSomeProperty());
// ...
statement.addBatch();
i++;
if (i % 1000 == 0 || i == entities.size())
statement.executeBatch(); // Execute every 1000 items.
它每 1000 个项目执行一次,因为某些 JDBC 驱动程序和/或 DB 可能对批处理长度有限制。
另见:
JDBC tutorial - Using PreparedStatement JDBC tutorial - Using Statement Objects for Batch Updates【讨论】:
如果您将它们放入事务中,您的插入速度会更快...即用connection.setAutoCommit(false);
和connection.commit();
download.oracle.com/javase/tutorial/jdbc/basics/… 包装
如果有 999 个项目,看起来你可以执行一个空批处理。
@electricalbah 它将正常执行,因为i == entities.size()
这是另一个关于使用准备好的语句将批处理作业放在一起的好资源。 viralpatel.net/blogs/batch-insert-in-java-jdbc
@AndréPaulo:任何适用于预准备语句的 SQL INSERT。有关基本示例,请参阅 JDBC 教程链接。这与具体问题无关。以上是关于Java:使用 PreparedStatement 将多行插入 MySQL的主要内容,如果未能解决你的问题,请参考以下文章
java中的PreparedStatement.addBatch有啥限制吗?
使用 PreparedStatement 登录在 Java 中不起作用 [重复]
Java、PreparedStatement、TransactSQL (MS SQL)、最后插入 ID
Java 的 PreparedStatement 是如何工作的?