title: How to do bulk (multi row) inserts with JpaRepository?
body: <p>When calling the <code>saveAll</code> method of my <code>JpaRepository</code> with a long <code>List&lt;Entity&gt;</code> from the service layer, trace logging of Hibernate shows single SQL statements being issued per entity.</p>
  
  <p>Can I force it to do a bulk insert (i.e. multi-row) without needing to manually fiddle with <code>EntityManger</code>, transactions etc. or even raw SQL statement strings?</p>
  
  <p>With multi-row insert I mean not just transitioning from:</p>
  
  <pre><code>start transaction
  INSERT INTO table VALUES (1, 2)
  end transaction
  start transaction
  INSERT INTO table VALUES (3, 4)
  end transaction
  start transaction
  INSERT INTO table VALUES (5, 6)
  end transaction
  </code></pre>
  
  <p>to:</p>
  
  <pre><code>start transaction
  INSERT INTO table VALUES (1, 2)
  INSERT INTO table VALUES (3, 4)
  INSERT INTO table VALUES (5, 6)
  end transaction
  </code></pre>
  
  <p>but instead to:</p>
  
  <pre><code>start transaction
  INSERT INTO table VALUES (1, 2), (3, 4), (5, 6)
  end transaction
  </code></pre>
  
  <p>In PROD I'm using CockroachDB, and the difference in performance is significant.</p>
  
  <p>Below is a minimal example that reproduces the problem (H2 for simplicity).</p>
  
  <hr>
  
  <p><code>./src/main/kotlin/ThingService.kt</code>:</p>
  
  <pre><code>package things
  
  import org.springframework.boot.autoconfigure.SpringBootApplication
  import org.springframework.boot.runApplication
  import org.springframework.web.bind.annotation.RestController
  import org.springframework.web.bind.annotation.GetMapping
  import org.springframework.data.jpa.repository.JpaRepository
  import javax.persistence.Entity
  import javax.persistence.Id
  import javax.persistence.GeneratedValue
  
  interface ThingRepository : JpaRepository&lt;Thing, Long&gt; {
  }
  
  @RestController
  class ThingController(private val repository: ThingRepository) {
      @GetMapping("/test_trigger")
      fun trigger() {
          val things: MutableList&lt;Thing&gt; = mutableListOf()
          for (i in 3000..3013) {
              things.add(Thing(i))
          }
          repository.saveAll(things)
      }
  }
  
  @Entity
  data class Thing (
      var value: Int,
      @Id
      @GeneratedValue
      var id: Long = -1
  )
  
  @SpringBootApplication
  class Application {
  }
  
  fun main(args: Array&lt;String&gt;) {
      runApplication&lt;Application&gt;(*args)
  }
  </code></pre>
  
  <hr>
  
  <p><code>./src/main/resources/application.properties</code>:</p>
  
  <pre><code>jdbc.driverClassName = org.h2.Driver
  jdbc.url = jdbc:h2:mem:db
  jdbc.username = sa
  jdbc.password = sa
  
  hibernate.dialect=org.hibernate.dialect.H2Dialect
  hibernate.hbm2ddl.auto=create
  
  spring.jpa.generate-ddl = true
  spring.jpa.show-sql = true
  
  spring.jpa.properties.hibernate.jdbc.batch_size = 10
  spring.jpa.properties.hibernate.order_inserts = true
  spring.jpa.properties.hibernate.order_updates = true
  spring.jpa.properties.hibernate.jdbc.batch_versioned_data = true
  </code></pre>
  
  <hr>
  
  <p><code>./build.gradle.kts</code>:</p>
  
  <pre><code>import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
  
  plugins {
      val kotlinVersion = "1.2.30"
      id("org.springframework.boot") version "2.0.2.RELEASE"
      id("org.jetbrains.kotlin.jvm") version kotlinVersion
      id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion
      id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion
      id("io.spring.dependency-management") version "1.0.5.RELEASE"
  }
  
  version = "1.0.0-SNAPSHOT"
  
  tasks.withType&lt;KotlinCompile&gt; {
      kotlinOptions {
          jvmTarget = "1.8"
          freeCompilerArgs = listOf("-Xjsr305=strict")
      }
  }
  
  repositories {
      mavenCentral()
  }
  
  dependencies {
      compile("org.springframework.boot:spring-boot-starter-web")
      compile("org.springframework.boot:spring-boot-starter-data-jpa")
      compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
      compile("org.jetbrains.kotlin:kotlin-reflect")
      compile("org.hibernate:hibernate-core")
      compile("com.h2database:h2")
  }
  </code></pre>
  
  <hr>
  
  <p>Run:</p>
  
  <pre><code>./gradlew bootRun
  </code></pre>
  
  <p>Trigger DB INSERTs:</p>
  
  <pre><code>curl http://localhost:8080/test_trigger
  </code></pre>
  
  <p>Log output:</p>
  
  <pre><code>Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: select thing0_.id as id1_0_0_, thing0_.value as value2_0_0_ from thing thing0_ where thing0_.id=?
  Hibernate: call next value for hibernate_sequence
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  Hibernate: insert into thing (value, id) values (?, ?)
  </code></pre>