Hướng dẫn Java JDBC, Xử lý batch trong JDBC, Code ví dụ Batch Insert

Hướng dẫn Java JDBC, Xử lý batch trong JDBC, Code ví dụ Batch Insert

1. JDBC Batch là gì?

Trong JDBC, mỗi lần bạn thực hiện insert, update, delete hoặc select, ứng dụng sẽ kết nối tới database một lần.

Giả sử, bạn cần insert 10000 bản ghi vào database

Theo cách thông thường, chúng ta thực hiện insert 10000 lần thì sẽ phải kết nối tới database 10000 lần để thao tác với database –> tốn rất nhiều thời gian

-> Ý tưởng dùng Batch (xử lý theo mẻ), tức là chúng ta sẽ kết nối 1 lần nhưng insert 10000 bản ghi cùng lúc như vậy sẽ tiết kiệm được thời gian của 9999 kết nối tới database.

Batch size:

Khi dùng batch xảy ra 1 vấn đề đó là nếu bạn insert 1 lần 10000 bản ghi thì sẽ tốn rất nhiều bộ nhớ, nếu số lượng bản ghi nhiều hơn có thể hệ thống sẽ bị quá tải

–> ta lại chia nhỏ ra, ví dụ mỗi lần kết nối tới database sẽ insert 100 bản ghi (con số 100 gọi là batch size) như thế vừa đảm bảo tốc độ, vừa đảm bảo bộ nhớ

Hibernate Batch Processing là gì? Batch Processing trong Hibernate

2. Code ví dụ thực hiện insert với JDBC Batch

Ở ví dụ này mình sử dụng:

Tạo database ‘demo-jdbc-batch’

CREATE SCHEMA `demo-jdbc-batch` ;

Tạo table ‘user_info’, cột id là khóa chính (primary key) và tự tăng

CREATE TABLE `demo-jdbc-batch`.`user_info` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NULL,
  `address` VARCHAR(255) NULL,
  PRIMARY KEY (`id`));

Hướng dẫn Java JDBC, Xử lý batch trong JDBC, Code ví dụ Batch Insert

Tạo project maven:

Thư viện sử dụng:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>stackjava.com</groupId>
  <artifactId>DemoJDBC</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
  </dependencies>
</project>

Class entity

package stackjava.com.demojdbcbatch.demo;

public class User {
  private int id;
  private String name;
  private String address;

  public User() {
  }

  public User(int id, String name, String address) {
    this.id = id;
    this.name = name;
    this.address = address;
  }

  public User(String name, String address) {
    this.name = name;
    this.address = address;
  }

  // getter - setter

}

Thực hiện ví dụ insert 10000 bản ghi với Statement sử dụng batch và không sử dụng batch

package stackjava.com.demojdbcbatch.demo;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class DemoStatementBatch {
  public static List<User> init() {
    List<User> listUser = new ArrayList<User>();
    for (int i= 0; i< 10000; i++) {
      listUser.add(new User("name"+i,"address"+i));
    }
    
    return listUser;		
  }
  
  public static void main(String[] args) throws SQLException, ClassNotFoundException {
    demoInsertWithoutBatch();
    System.out.println("---------------------------------------------------------------");
    demoInsertWithBatch(200);
  }
  
  public static void demoInsertWithBatch(int batchSize) throws SQLException, ClassNotFoundException {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo-jdbc-batch", "root", "admin1234");
    Statement statement = connection.createStatement();
    connection.setAutoCommit(false);
    long startTime = System.currentTimeMillis();
    List<User> listUser = init();
    System.out.println("Batch - Inserting... ");
    for (int i = 0; i< listUser.size(); i++) {
        String sql = "INSERT INTO user_info (name, address) VALUES ('"+listUser.get(i).getName()+"', '"+listUser.get(i).getAddress()+"');";
        statement.addBatch(sql);
        if (i % batchSize == 0) {
        	statement.executeBatch();
        }
    }
    statement.executeBatch();
    connection.commit();
    long endTime = System.currentTimeMillis();
    System.out.println("Total time: " + (endTime - startTime));
    connection.close();
  }
  
  public static void demoInsertWithoutBatch() throws SQLException, ClassNotFoundException {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo-jdbc-batch", "root", "admin1234");
    Statement statement = connection.createStatement();
    long startTime = System.currentTimeMillis();
    List<User> listUser = init();
    System.out.println("Without Batch - Inserting... ");

    for (User user: listUser) {
      String sql = "INSERT INTO user_info (name, address) VALUES ('" +user.getName()+"', '" + user.getAddress()+ "');";
      statement.executeUpdate(sql);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Total time: " + (endTime - startTime));
    connection.close();
  }
}

Trường hợp insert sử dụng batch:

  • connection.setAutoCommit(false): tắt tự động commit, chúng ta sẽ thực hiện commit bằng tay.
  • statement.addBatch(sql): thêm câu SQL vào batch
  • statement.executeBatch(): xử lý batch, kết nối database và thực hiện tất cả các câu SQL trong batch (Sau khi thực hiện xong, bên trong batch sẽ trống)
  • Cứ mỗi lần số lượng câu sql trong batch bằng batch size sẽ thực hiện executeBatch

Demo:

Rõ ràng việc sử dụng batch tiết kiệm được rất nhiều thời gian.

Thực hiện ví dụ insert 10000 bản ghi với PreparedStatement sử dụng batch và không sử dụng batch

package stackjava.com.demojdbcbatch.demo;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class DemoPreparedStatementBatch {
  public static List<User> init() {
    List<User> listUser = new ArrayList<User>();
    for (int i = 0; i < 10000; i++) {
      listUser.add(new User("name" + i, "address" + i));
    }

    return listUser;
  }

  public static void main(String[] args) throws SQLException, ClassNotFoundException {
    demoInsertWithoutBatch();
    System.out.println("---------------------------------------------------------------");
    demoInsertWithBatch(200);
  }

  public static void demoInsertWithBatch(int batchSize) throws ClassNotFoundException, SQLException {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo-jdbc-batch", "root",
        "admin1234");
    long startTime = System.currentTimeMillis();
    List<User> listUser = init();
    String sql = "INSERT INTO user_info (name, address) VALUES (?, ?);";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    connection.setAutoCommit(false);

    System.out.println("Batch - Inserting... ");
    int count = 1;
    for (User user : listUser) {
      pstmt.setString(1, user.getName());
      pstmt.setString(2, user.getAddress());
      pstmt.addBatch();
      if (count % batchSize == 0) {
        pstmt.executeBatch();
        connection.commit();
      }
      count++;
    }
    pstmt.executeBatch();
    connection.commit();
    long endTime = System.currentTimeMillis();
    System.out.println("Done.");
    System.out.println("Total time: " + (endTime - startTime));
    connection.close();
  }

  public static void demoInsertWithoutBatch() throws ClassNotFoundException, SQLException {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo-jdbc-batch", "root",
        "admin1234");
    long startTime = System.currentTimeMillis();
    List<User> listUser = init();
    String sql = "INSERT INTO user_info (name, address) VALUES (?, ?);";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    
    System.out.println("Without batch - Inserting... ");
    
    int count = 1;
    for (User user : listUser) {
      pstmt.setString(1, user.getName() + count);
      pstmt.setString(2, user.getAddress() + count);
      pstmt.executeUpdate();
      count ++;
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Done.");
    System.out.println("Total time: " + (endTime - startTime));
    connection.close();
  }

}

Demo:

Hướng dẫn Java JDBC, Xử lý batch trong JDBC, Code ví dụ Batch Insert

Okay, Done!

Các bạn có thể thực hiện ví dụ batch khi update 10000 bản ghi, xóa 10000 bản ghi như trên.

Download code ví dụ trên tại đây

 

References:

https://stackoverflow.com/questions/14264953/how-is-jdbc-batch-update-helpful

stackjava.com