本ブログは移転しました。新しいブログは こちら です。

Spring JDBCのbatchUpdateはどのへんが効率的なのか

Overview

spring jdbc使っていて、batchUpdateというのがあることに気がついた。

ドキュメントを見ていると複数のデータを登録していくときは効率的っぽいことが書いてあったけど、どう効率的なのかがよくわからなかったのでし らべてみた

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html#batchUpdate-java.lang.String-org.springframework.jdbc.core.BatchPreparedStatementSetter- https://docs.spring.io/spring-framework/docs/3.0.0.RC3/spring-framework-reference/html/ch12s04.html

実際の動きを確認

最初にこんな感じのテーブルと、

CREATE TABLE product(
   	id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
   	division INT(10) NOT NULL,
   	created DATETIME NOT NULL,
   	name VARCHAR(20) NOT NULL,
   	PRIMARY KEY(id),
   	UNIQUE KEY uk_product (division, created)
   );

こんな感じのコードを用意してどんなSQLが発行されるか試してみた

   @Repository
   public class ProductRepositoryImpl implements ProductRepository {
       private final NamedParameterJdbcTemplate jdbcTemplate;
   
       public ProductRepositoryImpl(NamedParameterJdbcTemplate jdbcTemplate) {
           this.jdbcTemplate = jdbcTemplate;
       }
   
       @Override
       public void batchInsert(List<Product> producs) {
           String sql = "INSERT INTO product (division, created, name) "  
                   "VALUES(:division, :created, :name)";
           jdbcTemplate.batchUpdate(sql,
                   producs.stream()
                           .map(p ->
                                   new MapSqlParameterSource()
                                           .addValue("division", p.getDivision(), Types.INTEGER)
                                           .addValue("created", p.getCreated(), Types.TIMESTAMP)
                                           .addValue("name", p.getName(), Types.CHAR)
                           ) .toArray(SqlParameterSource[]::new)
           );
       }
   }

上記のコードを実行して、Mysqlのログから実際発行されたSQLを確認してみる

| 2019-06-08 18:44:31.192892 | test[test] @  [xxx.xx.xx.xx] |        69 |         0 | Query        | INSERT INTO product (division, created, name) VALUES(1, '2019-06-08 18:44:28.159', 'aa')                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
| 2019-06-08 18:44:31.196375 | test[test] @  [xxx.xx.xx.xx] |        69 |         0 | Query        | INSERT INTO product (division, created, name) VALUES(1, '2019-06-08 18:44:29.659', 'bb') 

まぁそりゃそうだろ感があるが、普通にInsert文が2発飛んでいる というわけで、Spring JDBCのコードを追っていく。

Spring JDBCのコードを確認

https://github.com/spring-projects/spring-framework/blob/master/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java#L930

上記のコードを読んでいくと、PreparedStatement#addBatchでSQLを追加していき、PreparedStatement#executeBatchでまとめてDBへ送信しているようだった。

どうやら、一度にクエリをDBへ送るという点で、普通にupdateをくるくる回すよりパフォーマンスが優れているということのようだ。

……と、調べ終わってからそれっぽいことが書いてあるドキュメント(Spring JDBCのじゃないけど)を見つけた。 https://docs.oracle.com/javase/jp/1.3/guide/jdbc/spec2/jdbc2.1.frame6.html

Contents