Spring JDBC – Xử lý batch processing trong Spring JDBC, Code ví dụ.

Spring JDBC – Xử lý batch processing trong Spring JDBC, Code ví dụ.

1. Batch Processing là gì?

Theo cách xử lý thông thường, mỗi lần connect tới database ta sẽ thực hiện một câu truy vấn SQL, ví dụ cần insert 2 bản ghi thì sẽ thực hiện connect database 2 lần –> trường hợp cần insert nhiều bản ghi sẽ tốn nhiều thời gian connect tới database.

Batch Processing là xử lý theo mẻ, tức là mỗi lần connect database ta sẽ insert, update, delete một nhóm bản ghi. Việc xử lý theo mẻ như vậy giúp ta tiết kiệm thời gian trong mỗi lần kết nối tới database.

(Xem lại: xử lý batch trong JDBC, xử lý batch trong Hibernate)

2. Code ví dụ xử lý batch trong Spring JDBC

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

Tạo database ‘demo-spring-jdbc-batch’ và table ‘user_info’

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

Spring JDBC - Xử lý batch processing trong Spring JDBC, Code ví dụ.

 

Tạo project maven:

Spring JDBC - Xử lý batch processing trong Spring JDBC, Code ví dụ.

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>SpringJDBC</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.45</version>
    </dependency>
  </dependencies>
</project>

File cấu hình spring config:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

  <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/demo-spring-jdbc-batch" />
    <property name="username" value="root" />
    <property name="password" value="admin1234" />
  </bean>

  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
  </bean>

</beans>

File entity:

package stackjava.com.springjdbc.entities;

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;
  }

  @Override
  public String toString() {
    return "User [id=" + id + ", name=" + name + ", address=" + address + "]";
  }

  // getter - setter

}

Thực hiện demo một số cách xử lý batch trong spring jdbc (xử lý batch với JdbcTemplate)

Có khá nhiều cách để thực hiện xử lý batch với JdbcTemplate:

1.  batchUpdate(String... sql): truyền nhiều câu sql vào method batchUpdate, tất cả các câu sql được truyền vào sẽ được thực hiện cùng 1 lúc.

jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES ('Ronaldo', 'Portugal')",
    "INSERT INTO user_info (name, address) VALUES ('Messi', 'Argentina')");

2. batchUpdate(String sql, List<Object[]> batchArgs): truyền 1 câu sql vào method batchUpdate, List<Object[]> sẽ gồm danh sách các tham số cho từng câu sql:

jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)",
    Arrays.asList(new Object[] { "Ronaldo", "Portugal" }, new Object[] { "Messi", "Argentina" }));

3. batchUpdate(String sql, List <Object[]> batchArgs, int[] argTypes): giống với method bên trên nhưng chỉ rõ kiểu dữ liệu của mỗi tham số truyền vào.

jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)",
    Arrays.asList(new Object[] { "Ronaldo", "Portugal" }, new Object[] { "Messi", "Argentina" }),
    new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR });

4. batchUpdate(String sql, BatchPreparedStatementSetter pss)

final List<User> listUser = new ArrayList<User>();
listUser.add(new User("Ronaldo", "Portugal"));
listUser.add(new User("Messi", "Argentina" ));


jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)",
    new BatchPreparedStatementSetter() {

      public void setValues(PreparedStatement ps, int i) throws SQLException {
        ps.setString(1, listUser.get(i).getName());
        ps.setString(2, listUser.get(i).getAddress());
      }

      public int getBatchSize() {
        return listUser.size();
      }
    });

5. batchUpdate(String sql, Collection<T> batchArgs, int batchSize, ParameterizedPreparedStatementSetter <T> pss)

final List<User> listUser = new ArrayList<User>();
listUser.add(new User("Ronaldo", "Portugal"));
listUser.add(new User("Messi", "Argentina" ));


jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)", listUser, listUser.size(),
    new ParameterizedPreparedStatementSetter<User>() {
      public void setValues(PreparedStatement ps, User user) throws SQLException {
        ps.setString(1, user.getName());
        ps.setString(2, user.getAddress());
      }
    });

 

package stackjava.com.springjdbc.mainapp;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter;

import stackjava.com.springjdbc.entities.User;

public class Demo {

  public static void main(String[] args) throws SQLException {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");

    // do not use batch
    jdbcTemplate.update("INSERT INTO user_info (name, address) VALUES ('Songuku', 'Japanese')");

    // batchUpdate(String... sql)
    jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES ('Capital', 'USA')",
        "INSERT INTO user_info (name, address) VALUES ('Lee Byung Hun', 'Korea')",
        "INSERT INTO user_info (name, address) VALUES ('Jason Statham', 'England')");

    
    // batchUpdate(String sql, List<Object[]> batchArgs)
    jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)",
        Arrays.asList(new Object[] { "Micky", "Hollywood" }, new Object[] { "Donal Duck", "Hollywood" }));

    
    // batchUpdate(String sql, List <Object[]> batchArgs, int[] argTypes)
    jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)",
        Arrays.asList(new Object[] { "Micky", "Hollywood" }, new Object[] { "Donal Duck", "Hollywood" }),
        new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR });

    
    final List<User> listUser = new ArrayList<User>();
    listUser.add(new User("Tom", "house"));
    listUser.add(new User("Jerry", "house"));
    listUser.add(new User("Donal Trump", "house"));

    
    // batchUpdate(String sql, BatchPreparedStatementSetter pss)
    jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)",
        new BatchPreparedStatementSetter() {

          public void setValues(PreparedStatement ps, int i) throws SQLException {
            ps.setString(1, listUser.get(i).getName());
            ps.setString(2, listUser.get(i).getAddress());
          }

          public int getBatchSize() {
            return listUser.size();
          }
        });

    
    
    // batchUpdate(String sql, Collection<T> batchArgs, int batchSize, ParameterizedPreparedStatementSetter <T> pss)
    jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)", listUser, listUser.size(),
        new ParameterizedPreparedStatementSetter<User>() {
          public void setValues(PreparedStatement ps, User user) throws SQLException {
            ps.setString(1, user.getName());
            ps.setString(2, user.getAddress());
          }
        });

    
    ((ClassPathXmlApplicationContext) ctx).close();
    System.out.println("Done!");
    
  }
  
}

Kết quả:

Code so sánh tốc độ khi insert 10000 bản vào database sử dụng batch và không sử dụng batch.

package stackjava.com.springjdbc.mainapp;

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

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter;

import stackjava.com.springjdbc.entities.User;

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

    return listUser;
  }

  public static void main(String[] args) throws SQLException {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
    
    long startTime = System.currentTimeMillis();
    String sql = "INSERT INTO user_info (name, address) VALUES (?, ?);";
    List<User> listUser = init();
    for (User user: listUser) {
      jdbcTemplate.update(sql, user.getName(), user.getAddress());
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Insert without batch: " + (endTime - startTime));
    
    startTime = System.currentTimeMillis();
    jdbcTemplate.batchUpdate("INSERT INTO user_info (name, address) VALUES (?, ?)", listUser, 50,
        new ParameterizedPreparedStatementSetter<User>() {
      public void setValues(PreparedStatement ps, User user) throws SQLException {
        ps.setString(1, user.getName());
        ps.setString(2, user.getAddress());
      }
    });
    
    endTime = System.currentTimeMillis();
    System.out.println("Insert with batch: " + (endTime - startTime));
    
    ((ClassPathXmlApplicationContext) ctx).close();
    System.out.println("Done!");

  }
  
}

Kết quả:

Sử dụng batch tiết kiệm thời gian hơn rất nhiều.

Spring JDBC – Xử lý batch processing trong Spring JDBC, Code ví dụ.

Okay, Done!

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

 

References:

https://docs.spring.io/spring/docs/3.0.0.M4/reference/html/ch12s04.html

stackjava.com