Comment faire en vrac (multi-lignes) insère avec JpaRepository?
Lors de l'appel de la saveAll
méthode de mon JpaRepository
avec une longue List<Entity>
de la couche de service, la journalisation du suivi de Hibernate montre unique SQL émis par l'entité.
Puis-je le forcer à faire une insertion en bloc (c'est à dire multi-ligne) sans avoir à manuellement tripoter EntityManger
, entre autres. ou bien crus instruction SQL cordes?
Avec multi-ligne insérez je veux dire, pas seulement la transition à partir de:
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
à:
start transaction
INSERT INTO table VALUES (1, 2)
INSERT INTO table VALUES (3, 4)
INSERT INTO table VALUES (5, 6)
end transaction
mais au lieu de:
start transaction
INSERT INTO table VALUES (1, 2), (3, 4), (5, 6)
end transaction
En PROD, je suis en utilisant CockroachDB, et la différence de performance est importante.
Ci-dessous est un exemple minimal qui reproduit le problème (H2 pour des raisons de simplicité).
./src/main/kotlin/ThingService.kt
:
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<Thing, Long> {
}
@RestController
class ThingController(private val repository: ThingRepository) {
@GetMapping("/test_trigger")
fun trigger() {
val things: MutableList<Thing> = 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<String>) {
runApplication<Application>(*args)
}
./src/main/resources/application.properties
:
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
./build.gradle.kts
:
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<KotlinCompile> {
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")
}
Exécuter:
./gradlew bootRun
Déclencher DB INSERTs:
curl http://localhost:8080/test_trigger
Journal de sortie:
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 (?, ?)
- Veuillez vérifier ma réponse, j'espère qu'elle sera utile: stackoverflow.com/a/50694902/5380322
- Merci, mais je fais déjà cela (s'accumulent dans une liste et de l'appel de
saveAll
. J'ai juste ajouté un code minimal exemple à reproduire le problème. - Avez-vous mis
hibernate.jdbc.batch_size
propriété? - Oui. (voir ci-dessus)
- C'est incorrect, il doit être de cette forme:
spring.jpa.properties.hibernate.jdbc.batch_size
- Merci, rieckpil déjà mentioned et que j'ai modifié mon code en conséquence. Cependant, il n'est toujours pas de traitement par lots.
Vous devez vous connecter pour publier un commentaire.
Pour obtenir un bulk insert avec Sring de Démarrage et le Printemps de Données JPA vous avez seulement besoin de deux choses:
définir l'option
spring.jpa.properties.hibernate.jdbc.batch_size
à s'approprier les valeurs dont vous avez besoin (par exemple: 20).utilisation
saveAll()
méthode de votre repo avec la liste des entités préparé pour l'insertion.Exemple de travail est ici.
Sur la transformation de l'instruction insert en quelque chose comme ceci:
l'exemple est disponible dans PostgreSQL: vous pouvez définir l'option
reWriteBatchedInserts
de vrai dans la chaîne de connexion jdbc:puis pilote jdbc ne cette transformation.
Informations supplémentaires sur le traitement par lots, vous pouvez trouver ici.
Mis à JOUR
Projet de démonstration dans Kotlin: sb-kotlin-lot-insert-démo
Mis à JOUR
git clone https://github.com/Cepr0/sb-kotlin-batch-insert-demo
,cd sb-kotlin-batch-insert-demo
etmvn package
mais finissent avec l'erreur suivante: gist.github.com/Dobiasd/7f1163110b52876f171d43e17af0853csave()
méthode...Les questions sous-jacentes est le code suivant dans SimpleJpaRepository:
En plus de la taille du lot des paramètres de propriété, vous devez vous assurer que la classe SimpleJpaRepository appels persistent et se confondent pas. Il ya quelques approches pour résoudre ce problème: l'utilisation d'un
@Id
générateur qui n'a pas de séquence requête, commeOu en forçant la persistance de traiter les dossiers en tant que nouvelles en ayant votre entité mettre en œuvre Permanent et en remplaçant la
isNew()
appelOu remplacer le
save(List)
et utiliser le gestionnaire de l'entité d'appelpersist()
Le code ci-dessus est basé sur les liens suivants:
@Generated
@Id
valeurs à l'aide de laPersistable
méthode. Le lot est exécuté seulement quand je régler manuellement laid
le terrain par ma propre logique. Si je me fie@Generated
pour monLong
id
propriété, alors les instructions ne sont pas de course dans les lots. Tous les liens partagés par vous de ne pas utiliser@Generated
type de stratégie avecPersistable
méthode. J'ai même vérifié le Github le code de lien qui est fourni dans le 2ème lien, mais c'est aussi de l'affectation de laid
propriété manuellement.Vous pouvez configurer Hibernate faire en vrac DML. Jetez un oeil à Spring Data JPA - simultanée des insertions/mises à jour. Je pense que l'article 2 de la réponse pourrait résoudre votre problème:
Mise à JOUR: Vous devez définir les propriétés hibernate différemment dans votre
application.properties
fichier. Ils sont sous les noms:spring.jpa.properties.*
. Un exemple pourrait ressembler à ce qui suit:java.sql.PreparedStatement
dans mon dossier de candidature et l'envoyer à l'aide de la cruejava.sql.Connection
de lajavax.sql.DataSource
.