Example of batch inserting in Spring Data JPA application with two different id generation strategies
When persisting entities, especially when batching, if primary key is known before persisting action, then use
@GeneratedValue
and @Prepresist
to set the primary key and avoid N + 1 SELECT issue
1. Entity Id is set in method annotated with @PrePersist
and entity id field is annotated with @GeneratedValue
Run this application with Spring profile prepersist
and see logs:
2021-10-28 12:17:36.520 INFO 718350 --- [ main] jdbc.sqlonly : batching 5 statements: 1: insert into prepersist$model_with_generated_value (number, id) values
(0, '<byte[]>') 2: insert into prepersist$model_with_generated_value (number, id) values (1,
'<byte[]>') 3: insert into prepersist$model_with_generated_value (number, id) values (2, '<byte[]>')
4: insert into prepersist$model_with_generated_value (number, id) values (3, '<byte[]>') 5:
insert into prepersist$model_with_generated_value (number, id) values (4, '<byte[]>')
2021-10-28 12:17:36.523 INFO 718350 --- [ main] jdbc.sqlonly : batching 5 statements: 1: insert into prepersist$model_with_generated_value (number, id) values
(5, '<byte[]>') 2: insert into prepersist$model_with_generated_value (number, id) values (6,
'<byte[]>') 3: insert into prepersist$model_with_generated_value (number, id) values (7, '<byte[]>')
4: insert into prepersist$model_with_generated_value (number, id) values (8, '<byte[]>') 5:
insert into prepersist$model_with_generated_value (number, id) values (9, '<byte[]>')
2021-10-28 12:17:36.525 INFO 718350 --- [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
41746 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
4643056 nanoseconds spent preparing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC statements;
3110101 nanoseconds spent executing 2 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
21529955 nanoseconds spent executing 1 flushes (flushing a total of 10 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
Run this application with Spring profile other
and see in logs that
Hibernate adds extra select query for each persisted entity:
2021-10-28 12:19:32.026 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.028 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.033 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.034 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.035 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.035 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.035 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.036 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.036 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.036 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.036 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.037 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.037 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.037 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.037 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.037 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.038 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.038 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.038 INFO 718556 --- [ main] jdbc.sqlonly : select noprepersi0_.id as id1_0_0_, noprepersi0_.number as number2_0_0_ from no_pre_persist$model_without_generated_value
noprepersi0_ where noprepersi0_.id='<byte[]>'
2021-10-28 12:19:32.038 INFO 718556 --- [ main] jdbc.resultsettable :
|---|-------|
|id |number |
|---|-------|
|---|-------|
2021-10-28 12:19:32.045 INFO 718556 --- [ main] jdbc.sqlonly : batching 5 statements: 1: insert into no_pre_persist$model_without_generated_value (number,
id) values (0, '<byte[]>') 2: insert into no_pre_persist$model_without_generated_value (number,
id) values (1, '<byte[]>') 3: insert into no_pre_persist$model_without_generated_value (number,
id) values (2, '<byte[]>') 4: insert into no_pre_persist$model_without_generated_value (number,
id) values (3, '<byte[]>') 5: insert into no_pre_persist$model_without_generated_value (number,
id) values (4, '<byte[]>')
2021-10-28 12:19:32.047 INFO 718556 --- [ main] jdbc.sqlonly : batching 5 statements: 1: insert into no_pre_persist$model_without_generated_value (number,
id) values (5, '<byte[]>') 2: insert into no_pre_persist$model_without_generated_value (number,
id) values (6, '<byte[]>') 3: insert into no_pre_persist$model_without_generated_value (number,
id) values (7, '<byte[]>') 4: insert into no_pre_persist$model_without_generated_value (number,
id) values (8, '<byte[]>') 5: insert into no_pre_persist$model_without_generated_value (number,
id) values (9, '<byte[]>')
2021-10-28 12:19:32.049 INFO 718556 --- [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
43285 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
2119547 nanoseconds spent preparing 11 JDBC statements;
3421528 nanoseconds spent executing 10 JDBC statements;
2152100 nanoseconds spent executing 2 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
7743331 nanoseconds spent executing 1 flushes (flushing a total of 10 entities and 0 collections);
0 nanoseconds