Batchinnsetting / oppdatering med dvalemodus / JPA

1. Oversikt

I denne opplæringen vil vi se på hvordan vi kan sette inn eller oppdatere enheter ved hjelp av dvalemodus / JPA.

Batching lar oss sende en gruppe SQL-setninger til databasen i en enkelt nettverksanrop. På denne måten kan vi optimalisere nettverks- og minnebruk av applikasjonen vår.

2. Oppsett

2.1. Eksempel på datamodell

La oss se på eksempeldatamodellen som vi skal bruke i eksemplene.

For det første lager vi en Skole enhet:

@Entity public class School {@Id @GeneratedValue (strategi = GenerationType.SEQUENCE) privat lang id; privat strengnavn; @OneToMany (mappedBy = "school") private Listelev; // Getters og setters ...}

Hver Skole vil ha null eller mer Students:

@Entity public class Student {@Id @GeneratedValue (strategi = GenerationType.SEQUENCE) privat lang id; privat strengnavn; @ManyToOne privatskole; // Getter og setter ...}

2.2. Spore SQL-spørringer

Når du kjører eksemplene våre, må vi bekrefte at utsettings- / oppdateringsuttalelser faktisk sendes i grupper. Dessverre kan vi ikke forstå fra dvalemodus-logguttalelser om SQL-setninger er grupperte eller ikke. På grunn av dette bruker vi en datakildeproxy for å spore dvalemodus / JPA SQL-setninger:

privat statisk klasse ProxyDataSourceInterceptor implementerer MethodInterceptor {private final DataSource dataSource; public ProxyDataSourceInterceptor (final DataSource dataSource) {this.dataSource = ProxyDataSourceBuilder.create (dataSource) .name ("Batch-Insert-Logger") .asJson (). countQuery (). logQueryToSysOut (). build (); } // Andre metoder ...}

3. Standard atferd

Dvalemodus aktiverer ikke batching som standard. Dette betyr at den sender en egen SQL-setning for hver innsettings- / oppdateringsoperasjon:

@Transactional @Test offentlig ugyldig nårNotConfigured_ThenSendsInsertsSeparately () {for (int i = 0; i <10; i ++) {School school = createSchool (i); entityManager.persist (skole); } entityManager.flush (); }

Her har vi holdt på 10 Skole enheter. Hvis vi ser på spørringsloggene, kan vi se at Dvalemodus sender hver innsettingserklæring separat:

"querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School1", "1"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School2", "2"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School3", "3"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School4", "4"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School5", "5"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School6", "6"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School7", "7"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School8", "8"]] "querySize": 1, "batchSize": 0, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], " params ": [[" School9 "," 9 "]]" querySize ": 1," batchSize ": 0," query ": [" sett inn i skolens (navn, id) verdier (?,?) "]," params ": [[" School10 "," 10 "]]

Derfor bør vi konfigurere dvalemodus for å aktivere batching. For dette formålet, vi burde sette dvalemodus.jdbc.batch_size eiendom til et tall større enn 0.

Hvis vi skaper EntityManager manuelt, bør vi legge til dvalemodus.jdbc.batch_size til dvaleegenskapene:

public Properties hibernateProperties () {Properties properties = new Properties (); properties.put ("dvalemodus.jdbc.batch_size", "5"); // Andre egenskaper ... returegenskaper; }

Hvis vi bruker Spring Boot, kan vi definere det som en applikasjonsegenskap:

spring.jpa.properties.hibernate.jdbc.batch_size = 5

4. Buntinnsats for enkeltbord

4.1. Batchinnsats uten eksplisitt skylling

La oss først se på hvordan vi kan bruke batchinnsatser når vi bare har å gjøre med en enhetstype.

Vi bruker forrige kodeeksempel, men denne gangen er batching aktivert:

@Transactional @Test public void whenInsertingSingleTypeOfEntity_thenCreatesSingleBatch () {for (int i = 0; i <10; i ++) {School school = createSchool (i); entityManager.persist (skole); }}

Her har vi holdt på 10 Skole enheter. Når vi ser på loggene, kan vi verifisere at dvalemodus sender innsettingsuttalelser i grupper:

"batch": true, "querySize": 1, "batchSize": 5, "query": ["insert into school (name, id) values ​​(?,?)"], "params": [["School1" , "1"], ["School2", "2"], ["School3", "3"], ["School4", "4"], ["School5", "5"]] "batch": true, "querySize": 1, "batchSize": 5, "query": ["insert into school (name, id) values ​​(?,?)"], "params": [["School6", "6" ], ["School7", "7"], ["School8", "8"], ["School9", "9"], ["School10", "10"]]

En viktig ting å nevne her er minneforbruket. Når vi vedvarer en enhet, lagrer dvalemodus den i utholdenhetskonteksten. For eksempel, hvis vi vedvarer 100.000 enheter i en transaksjon, vil vi ende opp med å ha 100.000 enhetsforekomster i minnet, muligens forårsake en OutOfMemoryException.

4.2. Batchinnsats med eksplisitt spyling

Nå skal vi se på hvordan vi kan optimalisere minnebruk under batching-operasjoner. La oss grave dypt inn i utholdenhetskontekstens rolle.

Først og fremst lagrer utholdenhetskonteksten nyopprettede enheter og også de modifiserte i minnet. Dvalemodus sender disse endringene til databasen når transaksjonen synkroniseres. Dette skjer vanligvis ved slutten av en transaksjon. Derimot, ringer EntityManager.flush () utløser også en transaksjonssynkronisering.

For det andre tjener utholdenhetskonteksten som en enhetsbuffer, og dermed også referert til som første nivåbuffer. For å fjerne enheter i utholdenhetskonteksten, kan vi ringe EntityManager.clear ().

Så, for å redusere minnebelastningen under batching, kan vi ringe EntityManager.flush () og EntityManager.clear () på vår søknadskode, når batchstørrelse er nådd:

@Transactional @Test offentlig ugyldig nårFlushingAfterBatch_ThenClearsMemory () {for (int i = 0; i 0 && i% BATCH_SIZE == 0) {entityManager.flush (); entityManager.clear (); } Skoleskole = createSchool (i); entityManager.persist (skole); }}

Her skyller vi enhetene i utholdenhetskonteksten og får dermed dvalemodus til å sende spørsmål til databasen. Videre fjerner vi ved å tømme utholdenhetskonteksten Skole enheter fra minnet. Batching-oppførsel forblir den samme.

5. Batchinnsats for flere bord

La oss nå se hvordan vi kan konfigurere batchinnsatser når vi arbeider med flere enhetstyper i en transaksjon.

Når vi vil vedvare enhetene av flere typer, lager dvalemodus en annen batch for hver enhetstype. Dette er fordi det kan bare være én type enhet i en enkelt batch.

I tillegg når Hibernate samler innleggssetninger, oppretter det en ny batch når den møter en enhetstype som er forskjellig fra den i gjeldende batch. Dette er tilfelle, selv om det allerede er en batch for den enhetstypen:

@Transactional @ Test public void whenThereAreMultipleEntities_ThenCreatesNewBatch () {for (int i = 0; i 0 && i% BATCH_SIZE == 0) {entityManager.flush (); entityManager.clear (); } Skoleskole = createSchool (i); entityManager.persist (skole); Student firstStudent = createStudent (skole); Student secondStudent = createStudent (skole); entityManager.persist (firstStudent); entityManager.persist (secondStudent); }}

Her setter vi inn en Skole og tildele den to Students og gjenta denne prosessen 10 ganger.

I loggene ser vi at Hibernate sender Skole sett inn utsagn i flere partier av størrelse 1 mens vi bare forventet to partier av størrelse 5. Videre Student innsettingsuttalelser sendes også i flere partier av størrelse 2 i stedet for 4 grupper av størrelse 5:

"batch": true, "querySize": 1, "batchSize": 1, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School1" , "1"]] "batch": true, "querySize": 1, "batchSize": 2, "query": ["insert into student (name, school_id, id) values ​​(?,?,?)"] , "params": [["Student-School1", "1", "2"], ["Student-School1", "1", "3"]] "batch": true, "querySize": 1, "batchSize": 1, "query": ["sett inn i skolens (navn, id) verdier (?,?)"], "params": [["School2", "4"]] "batch": true, "querySize": 1, "batchSize": 2, "query": ["sett inn i student (navn, school_id, id) verdier (?,?,?)"], "params": [["Student-School2" , "4", "5"], ["Student-School2", "4", "6"]] "batch": true, "querySize": 1, "batchSize": 1, "query": [" sett inn i skolen (navn, id) verdier (?,?) "]," params ": [[" School3 "," 7 "]]" batch ": true," querySize ": 1," batchSize ": 2, "spørring": ["sett inn i studentens (navn, skole_id, id) verdier (?,?,?)"], "params": [["Student-School3", "7", "8"], [" Student-School3 "," 7 "," 9 "]] Andre logglinjer ...

For å gruppere alle innsettingsuttalelser av samme enhetstype, vi bør konfigurere hibernate.order_inserts eiendom.

Vi kan konfigurere dvalemodus-egenskapen manuelt ved hjelp av EntityManagerFactory:

public Properties hibernateProperties () {Properties properties = new Properties (); egenskaper.put ("dvalemodus.ordre_innsatser", "sant"); // Andre egenskaper ... returegenskaper; }

Hvis vi bruker Spring Boot, kan vi konfigurere egenskapen i application.properties:

spring.jpa.properties.hibernate.order_inserts = true

Etter å ha lagt til denne egenskapen, har vi 1 batch for Skole innlegg og 2 satser for Student innlegg:

"batch": true, "querySize": 1, "batchSize": 5, "query": ["insert into school (name, id) values ​​(?,?)"], "params": [["School6" , "16"], ["School7", "19"], ["School8", "22"], ["School9", "25"], ["School10", "28"]] "batch": true, "querySize": 1, "batchSize": 5, "query": ["insert into student (name, school_id, id) values ​​(?,?,?)"], "params": [["Student- School6 "," 16 "," 17 "], [" Student-School6 "," 16 "," 18 "], [" Student-School7 "," 19 "," 20 "], [" Student-School7 " , "19", "21"], ["Student-School8", "22", "23"]] "batch": true, "querySize": 1, "batchSize": 5, "query": [" sett inn verdiene til studentene (navn, skole_id, id) (?,?,?) "]," params ": [[" Student-School8 "," 22 "," 24 "], [" Student-School9 "," 25 "," 26 "], [" Student-School9 "," 25 "," 27 "], [" Student-School10 "," 28 "," 29 "], [" Student-School10 "," 28 " , "30"]]

6. Batchoppdatering

La oss nå gå videre til batchoppdateringer. I likhet med batchinnsatser kan vi gruppere flere oppdateringsuttalelser og sende dem til databasen på en gang.

For å aktivere dette, vi konfigurerer hibernate.order_updates og hibernate.jdbc.batch_versioned_data eiendommer.

Hvis vi lager vår EntityManagerFactory manuelt kan vi sette egenskapene programmatisk:

public Properties hibernateProperties () {Properties properties = new Properties (); properties.put ("dvalemodus.ordre_oppdateringer", "sant"); properties.put ("hibernate.batch_versioned_data", "true"); // Andre egenskaper ... returegenskaper; }

Og hvis vi bruker Spring Boot, legger vi bare til applikasjonen. Egenskaper:

spring.jpa.properties.hibernate.order_updates = true spring.jpa.properties.hibernate.batch_versioned_data = true

Etter å ha konfigurert disse egenskapene, bør dvalemodus gruppere oppdateringsuttalelser i grupper:

@Transactional @Test public void whenUpdatingEntities_thenCreatesBatch () {TypedQuery schoolQuery = entityManager.createQuery ("SELECT s from School s", School.class); Liste allSchools = schoolQuery.getResultList (); for (School school: allSchools) {school.setName ("Updated_" + school.getName ()); }}

Her har vi oppdatert skoleenheter, og Hibernate sender SQL-setninger i to grupper av størrelse 5:

"batch": true, "querySize": 1, "batchSize": 5, "query": ["update school set name =? where id =?"], "params": [["Updated_School1", "1" ], ["Updated_School2", "2"], ["Updated_School3", "3"], ["Updated_School4", "4"], ["Updated_School5", "5"]] "batch": true, "querySize ": 1," batchSize ": 5," query ": [" update school set name =? Where id =? "]," Params ": [[" Updated_School6 "," 6 "], [" Updated_School7 "," 7 "], [" Updated_School8 "," 8 "], [" Updated_School9 "," 9 "], [" Updated_School10 "," 10 "]]

7. @Id Generasjonsstrategi

Når vi vil bruke batching for innlegg / oppdateringer, bør vi være klar over den primære nøkkelgenereringsstrategien. Hvis enhetene våre bruker GenerationType.IDENTITY identifikatorgenerator, vil dvalemodus stille deaktivere batchinnsatser / oppdateringer.

Siden enheter i eksemplene våre bruker GenerationType.SEQUENCE identifikatorgenerator, dvalemodus muliggjør batchoperasjoner:

@Id @GeneratedValue (strategi = GenerationType.SEQUENCE) privat lang id;

8. Oppsummering

I denne artikkelen så vi på batchinnsatser og oppdateringer ved hjelp av Hibernate / JPA.

Sjekk ut kodeeksemplene for denne artikkelen på Github.


$config[zx-auto] not found$config[zx-overlay] not found