39

我现在正在使用批处理:

String query = "INSERT INTO table (id, name, value) VALUES (?, ?, ?)";
PreparedStatement ps = connection.prepareStatement(query);            
for (Record record : records) {
    ps.setInt(1, record.id);
    ps.setString(2, record.name);
    ps.setInt(3, record.value);
    ps.addBatch();
}
ps.executeBatch();

我只是想知道上面的代码是否等同于下面的代码。如果不是,哪个更快?

String query = "INSERT INTO table (id, name, value) VALUES ";
for (Record record : records) {
    query += "(" + record.id + ",'" + record.name + "'," + record.value + "),";
}
query = query.substring(1, query.length() - 1);
PreparedStatement ps = connection.prepareStatement(query);
ps.executeUpdate();
4

6 回答 6

43

关闭自动提交

executeBatchexecuteUpdate只要将autocommit设置为 false ,性能就会有所提高:

connection.setAutoCommit(false);  
PreparedStatement ps = connection.prepareStatement(query);            
for (Record record : records) {
    // etc.
    ps.addBatch();
}
ps.executeBatch();
connection.commit(); 
于 2012-08-17T20:20:41.127 回答
30

首先,使用查询字符串连接,您不仅会丢失 PreparedStatement 方法的原生类型转换,而且还容易受到数据库中执行的恶意代码的攻击。

其次,PreparedStatements 之前缓存在数据库本身中,这已经比普通 Statements 提供了非常好的性能改进。

于 2012-08-17T20:17:06.243 回答
3

如果要插入的项目数量很大,您可能会面临严重的性能问题。因此,更安全的是定义一个batch size,并在达到batch size时不断执行查询。

类似以下示例代码的内容应该可以工作。有关如何有效使用此代码的完整故事,请参阅此链接

private static void insertList2DB(List<String> list) {
        final int batchSize = 1000; //Batch size is important.
        Connection conn = getConnection();
        PreparedStatement ps = null;
        try {
            String sql = "INSERT INTO theTable (aColumn) VALUES (?)";
            ps = conn.prepareStatement(sql);

            int insertCount=0;
            for (String item : list) {
                ps.setString(1, item);
                ps.addBatch();
                if (++insertCount % batchSize == 0) {
                    ps.executeBatch();
                }
            }
            ps.executeBatch();

        } catch (SQLException e) {
            e.printStackTrace();
            System.exit(1);
        }
    finally {
        try {
            ps.close();
            conn.close();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
} 
于 2018-07-31T14:18:23.240 回答
1
public void insertBatch(final List<Record > records ) {

    String query = "INSERT INTO table (id, name, value) VALUES (?, ?, ?)";


    GenericDAO.getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() {


        @Override
        public void setValues(PreparedStatement ps, int i) throws SQLException {
              Record record = records .get(i);
              ps.setInt(1, record.id);
              ps.setString(2, record.name);
              ps.setInt(3, record.value);
        }

        @Override
        public int getBatchSize() {
            return records.size();
        }
    });
}
于 2014-04-30T11:53:59.763 回答
1

如果您的记录大小小于或等于 1000,则以下代码优于您的两个代码:

StringBuilder query = new StringBuilder("INSERT INTO table (id, name, value) VALUES ");

if (records.size() <= 1000) {
    
    for (int i = 0; i < records.size(); i++)
        query.append("(?, ?, ?), ");

    query = new StringBuilder(query.substring(1, query.length() - 1));

    PreparedStatement ps = connection.prepareStatement(query.toString());

    for (int i = 0; i < records.size(); i++) {
        ps.setInt((i * 3) + 1, record.id);
        ps.setString((i * 3) + 2, record.name);
        ps.setInt((i * 3) + 3, record.value);
    }
    
    ps.executeUpdate();
    
}

通过这种方式,您正在使用 PreparedStatement 并根据记录列表的大小在一个插入查询中使用多个值子句动态创建它

于 2021-01-06T06:35:54.183 回答
-7

我想这会做

String query = "INSERT INTO table (id, name, value) VALUES ";
for (Record record : records)
{
query += "(" + record.id + ",'" + record.name + "'," + record.value + "),";
query = query.substring(1, query.length() - 1);
PreparedStatement ps = connection.prepareStatement(query);
ps.executeUpdate();
}

因为您必须对要插入数据库的每条记录执行查询。

于 2012-08-17T20:48:46.467 回答