使用PreparedStatement实现批量数据的操作(代码优化)

本文介绍使用PreparedStatement进行批量数据插入的三种优化方法,并对比了不同方式下执行效率的变化。

使用PreparedStatement实现批量数据的操作(代码优化)

package com.shan.blob;

import com.shan.util.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;

/**
 * 使用PreparedStatement实现批量数据的操作
 * update、delete本身就具有批量操作的效果
 *
 * 此时的批量操作,主要值得是批量插入
 *
 *
 * @author shan
 * @date 2021/5/17
 */
public class InsertTest {
    public static void main(String[] args) {
//        testInsert1();
//        testInsert2();
        testInsert3();
    }


    public static void testInsert1(){
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            long start = System.currentTimeMillis();

            conn = JDBCUtils.getConnection();
            String sql = "insert into goods(name)values(?)";
            ps = conn.prepareStatement(sql);
            for(int i=0; i<=20000; i++){
                ps.setObject(1, "name_" + i);
                ps.execute();
            }
            long end = System.currentTimeMillis();
            System.out.println("花费的时间为:" + (end-start));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,ps);
        }

    }

    //优化的批量插入操作二
    /**
    * 1.addBatch()、executeBatch()、clearBatch()
    * 2.mysql服务器是关闭批处理的,我们需要一个参数,让mysql开启批处理的支持。
    *       ?rewriteBatchedStatement=true 写在配置文件的url后面
    * 3.使用更新的驱动 mysql-connector-java-5.1.37-bin.jar
     * */

    public static void testInsert2(){
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            long start = System.currentTimeMillis();

            conn = JDBCUtils.getConnection();
            String sql = "insert into goods(name)values(?)";
            ps = conn.prepareStatement(sql);
            for(int i=0; i<=20000; i++){
                ps.setObject(1, "name_" + i);

                //1. 攒 sql
                ps.addBatch();
                if(i%500 == 0){
                    //2.执行batch
                    ps.executeBatch();
                    //3.清空batch
                    ps.clearBatch();
                }


            }
            long end = System.currentTimeMillis();
            System.out.println("花费的时间为:" + (end-start));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,ps);
        }


    }
    //优化的批量插入操作三
    /**
     * 设置连接不允许自动提交
     * */

    public static void testInsert3(){
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            long start = System.currentTimeMillis();

            conn = JDBCUtils.getConnection();

            //设置不允许自动提交数据
            conn.setAutoCommit(false);

            String sql = "insert into goods(name)values(?)";
            ps = conn.prepareStatement(sql);
            for(int i=0; i<=20000; i++){
                ps.setObject(1, "name_" + i);

                //1. 攒 sql
                ps.addBatch();
                if(i%500 == 0){
                    //2.执行batch
                    ps.executeBatch();
                    //3.清空batch
                    ps.clearBatch();
                }


            }

            //提交数据
            conn.commit();

            long end = System.currentTimeMillis();
            System.out.println("花费的时间为:" + (end-start));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,ps);
        }


    }
}

<think>好的,用户现在想知道如何使用Java的PreparedStatement实现批量插入数据库。之前他们问了如何处理包含特殊符号的字符串插入Oracle的问题,现在可能是在实际开发中遇到了需要高效插入大量数据的需求。首先,用户提到参考了站内的引用,其中引用[2]和[3]都提到了批量插入的方法,比如使用addBatch和executeBatch。需要结合这些信息,给出具体的示例代码。用户之前的问题中,我推荐了使用PreparedStatement来防止SQL注入,现在他们可能想将这个最佳实践应用到批量操作中。需要强调批量插入同样应该使用PreparedStatement,既安全又高效。接下来,需要考虑不同的数据库,比如Oracle和MySQL,在批量插入时是否有不同的注意事项。例如,Oracle可能需要修改连接参数来优化批量处理,而MySQL则可以通过rewriteBatchedStatements参数提升性能。然后,要分步骤讲解如何实现批量插入。首先创建连接,准备SQL语句,然后循环添加参数并执行批处理。注意要演示addBatch和executeBatch的使用,以及事务管理的部分,比如关闭自动提交,最后提交事务。可能用户还会关心性能优化,比如批处理大小、连接参数设置等,这些需要简要提及。同时,要提醒在异常处理时进行回滚操作,保证数据一致性。最后,生成相关问题,帮助用户进一步了解如何处理大数据量插入、不同数据库的优化差异,以及事务管理的问题。确保问题与当前主题相关,并且扩展用户可能的需求。</think>### Java使用PreparedStatement实现批量插入的完整方案 #### 一、核心原理 通过`addBatch()`方法批量添加SQL参数,利用`executeBatch()`统一执行,可显著提升插入效率[^2]。该方法相比逐条插入可减少$N-1$次网络往返(假设插入$N$条数据),时间复杂度从$O(N)$降为$O(1)$[^3]。 --- #### 二、基础实现示例(Oracle/MySQL通用) ```java public class BatchInsertDemo { public static void main(String[] args) { String url = "jdbc:oracle:thin:@localhost:1521:ORCL"; String user = "username"; String password = "password"; int batchSize = 1000; // 每批次插入数量 try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement pstmt = conn.prepareStatement( "INSERT INTO employees (id, name, salary) VALUES (?, ?, ?)")) { conn.setAutoCommit(false); // 关闭自动提交 for (int i = 1; i <= 10000; i++) { pstmt.setInt(1, i); pstmt.setString(2, "Employee_" + i); pstmt.setDouble(3, 5000 + i * 100); pstmt.addBatch(); // 添加到批处理 if (i % batchSize == 0) { pstmt.executeBatch(); // 执行批次 conn.commit(); } } pstmt.executeBatch(); // 处理剩余数据 conn.commit(); } catch (SQLException e) { e.printStackTrace(); } } } ``` **关键优化点**: 1. 通过`setAutoCommit(false)`关闭自动提交提升性能 2. 分批次提交避免内存溢出 3. 使用try-with-resources自动释放连接 --- #### 三、不同数据优化差异 | 数据库 | 参数设置(连接URL) | 注意事项 | |--------|-------------------------------------|---------------------------------| | Oracle | `?rewriteBatchedStatements=true` | 需驱动版本≥ojdbc6 | | MySQL | `?useServerPrepStmts=true&cachePrepStmts=true` | 启用预编译语句缓存 | **Oracle特殊配置**: ```java // 添加批处理参数 Properties props = new Properties(); props.setProperty("user", user); props.setProperty("password", password); props.setProperty("defaultExecuteBatch", "1000"); // 设置默认批处理大小 Connection conn = DriverManager.getConnection(url, props); ``` --- #### 四、性能分析 假设插入$n$条数据,每条包含$m$个字段: $$总耗时 = t_{\text{网络}} \times \frac{n}{batchSize} + t_{\text{处理}} \times n \times m$$ 通过调整$batchSize$可找到最优值(通常500-2000之间)。测试表明:当$batchSize=1000$时,插入速度可达到单条插入的$50$倍以上[^3]。 --- #### 五、异常处理建议 ```java try { // 批处理操作... } catch (BatchUpdateException e) { int[] updateCounts = e.getUpdateCounts(); for (int i = 0; i < updateCounts.length; i++) { if (updateCounts[i] == Statement.EXECUTE_FAILED) { System.out.println("第" + (i+1) + "条数据插入失败"); } } conn.rollback(); // 回滚整个批次 } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值