10. 批量插入

10. 批量插入

前言

上一章节,我们使用 PreparedStatement 操作了 BLOB 字段,下面我们再来看看批量插入的操作。

批量插入

1.  批量执行SQL语句

当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率

JDBC的批量处理语句包括下面三个方法:

  • addBatch(String):添加需要批量处理的SQL语句或是参数;

  • executeBatch():执行批量处理语句;

  • clearBatch(): 清空缓存的数据

通常我们会遇到两种批量执行SQL语句的情况:

  • 多条SQL语句的批量处理;

  • 一个SQL语句的批量传参;

2.  高效的批量插入

举例:向数据表中插入20000条数据

  • 数据库中提供一个goods表。创建如下:

CREATE TABLE goods(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
  • 测试插入数据

mysql> insert into goods(name) values('test');
Query OK, 1 row affected (0.00 sec)

mysql> insert into goods(name) values('test1');
Query OK, 1 row affected (0.01 sec)

mysql> select * from goods;
+----+-------+
| id | NAME  |
+----+-------+
|  1 | test  |
|  2 | test1 |
+----+-------+
2 rows in set (0.00 sec)

mysql> 

2.1 实现层次一:使用Statement插入20000条数据

    //实现层次一:使用Statement
    //向数据表中插入20000条数据
    @Test
    public void test01() throws Exception {
        //1.获取连接
        Connection conn = JDBCUtils.getConnection();
        Statement st = conn.createStatement();

        //2.执行插入数据
//        String sql = "insert into goods(name) values('test1')";
//        st.executeUpdate(sql);
        //向数据表中插入20000条数据
        for (int i = 1; i <= 20000; i++) {
            String sql = "insert into goods(name) values('test " + i +"')";
            st.executeUpdate(sql);
        }
    }

测试执行如下:

image-20201021224530188

执行之后的mysql数据:

mysql> select * from goods limit 10;
+----+--------+
| id | NAME   |
+----+--------+
|  3 | test1  |
|  4 | test1  |
|  5 | test 1 |
|  6 | test 2 |
|  7 | test 3 |
|  8 | test 4 |
|  9 | test 5 |
| 10 | test 6 |
| 11 | test 7 |
| 12 | test 8 |
+----+--------+
10 rows in set (0.00 sec)

mysql> select count(1) from goods limit 10;
+----------+
| count(1) |
+----------+
|    20002 |
+----------+
1 row in set (0.00 sec)

2.2 实现层次二:使用PreparedStatement插入20000条数据

在上面可以看到耗时挺久的,下面我们改用 PreparedStatement 来插入数据看看。

//实现层次二:使用PreparedStatement插入20000条数据
@Test
public void test02() throws Exception {
    //1.记录执行开始时间
    long start = System.currentTimeMillis();

    //2.获取数据库连接
    Connection conn = JDBCUtils.getConnection();

    //3.预编译SQL
    String sql = "insert into goods(name) values(?)";
    PreparedStatement ps = conn.prepareStatement(sql);

    //4.设置插入数据以及执行
    for (int i = 1; i <= 20000; i++) {
        ps.setString(1, "test" + i);
        ps.executeUpdate();
    }

    //5.计算结束时间
    long end = System.currentTimeMillis();
    System.out.println("花费的时间为:" + (end - start));//

    //6.关闭资源
    JDBCUtils.closeResource(conn, ps);
}

测试执行如下:

image-20201021232054150

2.3 实现层次三:优化效率,将逐条SQL执行 改为 批处理执行

  • 配置mysql启用批处理执行 rewriteBatchedStatements=true

image-20201021233244936
user=root
password=*****
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
driverClass=com.mysql.cj.jdbc.Driver
  • 测试代码

/**
 * 实现层次三:使用PreparedStatement插入20000条数据
 * 修改1:使用 addBatch() / executeBatch() / clearBatch()
 * 修改2:mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
 *         ?rewriteBatchedStatements=true 写在配置文件的url后面
 *
 *
 * @throws Exception
 */
@Test
public void test03() throws Exception {
    //1.记录执行开始时间
    long start = System.currentTimeMillis();

    //2.获取数据库连接
    Connection conn = JDBCUtils.getConnection();

    //3.预编译SQL
    String sql = "insert into goods(name) values(?)";
    PreparedStatement ps = conn.prepareStatement(sql);

    //4.设置插入数据以及执行
    for (int i = 1; i <= 20000; i++) {
        ps.setString(1, "test" + i);

        //执行批处理
        //1.“攒”sql
        ps.addBatch();
        if(i % 500 == 0){ // 当达到500,则开始批处理
            //2.执行
            ps.executeBatch();
            //3.清空
            ps.clearBatch();
        }
    }

    //5.计算结束时间
    long end = System.currentTimeMillis();
    System.out.println("花费的时间为:" + (end - start));//

    //6.关闭资源
    JDBCUtils.closeResource(conn, ps);
}

执行测试如下:

image-20201021233428872

2.4 实现层次四:优化效率,将批处理的多次提交 设置为 最后统一 commit() 提交

image-20201021233743941
/**
 * 实现层次四:优化效率,将批处理的多次提交 设置为 最后统一 commit() 提交
 * 使用Connection 的 setAutoCommit(false)  /  commit()
 *
 * @throws Exception
 */
@Test
public void test04() throws Exception {
    //1.记录执行开始时间
    long start = System.currentTimeMillis();

    //2.获取数据库连接
    Connection conn = JDBCUtils.getConnection();

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

    //3.预编译SQL
    String sql = "insert into goods(name) values(?)";
    PreparedStatement ps = conn.prepareStatement(sql);

    //4.设置插入数据以及执行
    for (int i = 1; i <= 20000; i++) {
        ps.setString(1, "test" + i);

        //执行批处理
        //1.“攒”sql
        ps.addBatch();
        if(i % 500 == 0){ // 当达到500,则开始批处理
            //2.执行
            ps.executeBatch();
            //3.清空
            ps.clearBatch();
        }
    }

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

    //5.计算结束时间
    long end = System.currentTimeMillis();
    System.out.println("花费的时间为:" + (end - start));//

    //6.关闭资源
    JDBCUtils.closeResource(conn, ps);
}

测试执行如下:

image-20201021233840943