[Java JBDC] Tạo Batch Update với JdbcTemplate

Chào các bạn, trong bài viết này, chúng ta sẽ cùng tìm hiểu về cách sử dụng Spring JdbcTemplate để tạo batch insert, batch update và @Transactional

Kỹ thuật được sử dụng:
  • Spring Boot 2.1.2.RELEASE
  • Spring JDBC 5.1.4.RELEASE
  • Maven 3
  • Java 8

1. Batch Insert
1.1 Chạy một loạt câu SQLinsert cùng với nhau.

BookRepository.java
Java:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;

    public int[] batchInsert(List<Book> books) {

        return this.jdbcTemplate.batchUpdate(
            "insert into books (name, price) values(?,?)",
            new BatchPreparedStatementSetter() {

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

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

            });
    }

1.2 Nếu batch quá lớn, có thể chia nó thành các batch có kích thước nhỏ hơn.

BookRepository.java
Java:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter;

    public int[][] batchInsert(List<Book> books, int batchSize) {

        int[][] updateCounts = jdbcTemplate.batchUpdate(
                "insert into books (name, price) values(?,?)",
                books,
                batchSize,
                new ParameterizedPreparedStatementSetter<Book>() {
                    public void setValues(PreparedStatement ps, Book argument)
                        throws SQLException {
                        ps.setString(1, argument.getName());
                        ps.setBigDecimal(2, argument.getPrice());
                    }
                });
        return updateCounts;

    }

2. Batch Update
2.1 Giống câu SQL update.

BookRepository.java
Java:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;

    public int[] batchUpdate(List<Book> books) {

        return this.jdbcTemplate.batchUpdate(
                "update books set price = ? where id = ?",
                new BatchPreparedStatementSetter() {

                    public void setValues(PreparedStatement ps, int i)
                        throws SQLException {
                        ps.setBigDecimal(1, books.get(i).getPrice());
                        ps.setLong(2, books.get(i).getId());
                    }

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

                });

    }
2.2 Chạy theo batchSize.

BookRepository.java
Java:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter;

    public int[][] batchUpdate(List<Book> books, int batchSize) {

        int[][] updateCounts = jdbcTemplate.batchUpdate(
                "update books set price = ? where id = ?",
                books,
                batchSize,
                new ParameterizedPreparedStatementSetter<Book>() {
                    public void setValues(PreparedStatement ps, Book argument)
                        throws SQLException {
                        ps.setBigDecimal(1, argument.getPrice());
                        ps.setLong(2, argument.getId());
                    }
                });
        return updateCounts;

    }
3. Chạy thử
3.1 Tạo một bảng để test một loạt câu insert và update.

SpringBootApplication.java
Java:
startBookBatchUpdateApp(1000);

void startBookBatchUpdateApp(int size) {

    jdbcTemplate.execute("CREATE TABLE books(" +
            "id SERIAL, name VARCHAR(255), price NUMERIC(15, 2))");

    List<Book> books = new ArrayList();
    for (int count = 0; count < size; count++) {
        books.add(new Book(NameGenerator.randomName(20), new BigDecimal(1.99)));
    }

    // batch insert
    bookRepository.batchInsert(books);

    List<Book> bookFromDatabase = bookRepository.findAll();

    // count
    log.info("Total books: {}", bookFromDatabase.size());
    // random
    log.info("{}", bookRepository.findById(2L).orElseThrow(IllegalArgumentException::new));
    log.info("{}", bookRepository.findById(500L).orElseThrow(IllegalArgumentException::new));

    // update all books to 9.99
    bookFromDatabase.forEach(x -> x.setPrice(new BigDecimal(9.99)));

    // batch update
    bookRepository.batchUpdate(bookFromDatabase);

    List<Book> updatedList = bookRepository.findAll();

    // count
    log.info("Total books: {}", updatedList.size());
    // random
    log.info("{}", bookRepository.findById(2L).orElseThrow(IllegalArgumentException::new));
    log.info("{}", bookRepository.findById(500L).orElseThrow(IllegalArgumentException::new));

}
Kết quả
Code:
Total books: 1000
Book{id=2, name='FcRzgpauFtwfWibpzWog', price=1.99}
Book{id=500, name='htDvtGmksjfGmXGKOCaR', price=1.99}

Total books: 1000
Book{id=2, name='FcRzgpauFtwfWibpzWog', price=9.99}
Book{id=500, name='htDvtGmksjfGmXGKOCaR', price=9.99}

4. @Transactional
4.1 Với @Transactional, nếu có bất kỳ lỗi nào sẽ rollback toàn bộ, book sẽ không được thêm vào bảng.

BookRepository.java
Java:
@Transactional
public int[][] batchInsert(List<Book> books, int batchSize) {

    int[][] updateCounts = jdbcTemplate.batchUpdate(
            "insert into books (name, price) values(?,?)",
            books,
            batchSize,
            new ParameterizedPreparedStatementSetter<Book>() {
                public void setValues(PreparedStatement ps, Book argument) throws SQLException {
                    ps.setString(1, argument.getName());
                    ps.setBigDecimal(2, argument.getPrice());
                }
            });
    return updateCounts;

}

4.2 Thử batch insert 1000 book, dòng #500 có lỗi, và tất cả batch sẽ bị rollback, không có book nào được insert.

SpringBootApplication.java
Java:
startBookBatchUpdateRollBack(1000);

void startBookBatchUpdateRollBack(int size) {

    jdbcTemplate.execute("CREATE TABLE books(" +
            "id SERIAL, name VARCHAR(255), price NUMERIC(15, 2))");

    List<Book> books = new ArrayList();
    for (int count = 0; count < size; count++) {
        if (count == 500) {
            // Create an invalid data for id 500, test rollback
            // Name max 255, this book has length of 300
            books.add(new Book(NameGenerator.randomName(300), new BigDecimal(1.99)));
            continue;
        }
        books.add(new Book(NameGenerator.randomName(20), new BigDecimal(1.99)));
    }

    try {
        // with @Transactional, any error, entire batch will be rolled back
        bookRepository.batchInsert(books, 100);
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }

    List<Book> bookFromDatabase = bookRepository.findAll();

    // count = 0 , id 500 error, roll back all
    log.info("Total books: {}", bookFromDatabase.size());

}
Kết quả
Code:
PreparedStatementCallback; SQL [insert into books (name, price) values(?,?)]; Value too long for column "NAME VARCHAR(255)"

Total books: 0

Download Source Code
$ git clone https://github.com/mkyong/spring-boot.git
$ cd spring-jdbc



Cám ơn các bạn đã theo dõi. Hẹn gặp lại các bạn trong các bài viết sau :D

Bài viết tham khảo tại: https://mkyong.com/spring/spring-jdbctemplate-batchupdate-example/
 

Bình luận