Skip to content

Commit f4abe83

Browse files
committed
Add benchmarks.
1 parent be4718e commit f4abe83

File tree

6 files changed

+324
-80
lines changed

6 files changed

+324
-80
lines changed

pom.xml

Lines changed: 5 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
35

46
<modelVersion>4.0.0</modelVersion>
57

68
<groupId>org.springframework.data</groupId>
79
<artifactId>spring-data-relational-parent</artifactId>
8-
<version>4.0.0-1737-nullable-embedded-with-collection-574-composite-id-SNAPSHOT</version>
10+
<version>4.0.0-1737-nullable-embedded-with-collection-574-composite-id-SNAPSHOT
11+
</version>
912
<packaging>pom</packaging>
1013

1114
<name>Spring Data Relational Parent</name>
@@ -53,7 +56,6 @@
5356
<archunit.version>1.3.0</archunit.version>
5457

5558
<jmh.version>1.37</jmh.version>
56-
<mbr.version>0.4.0.BUILD-SNAPSHOT</mbr.version>
5759
</properties>
5860

5961
<inceptionYear>2017</inceptionYear>
@@ -165,87 +167,12 @@
165167
<profile>
166168
<id>jmh</id>
167169
<dependencies>
168-
<dependency>
169-
<groupId>com.github.mp911de.microbenchmark-runner</groupId>
170-
<artifactId>microbenchmark-runner-junit5</artifactId>
171-
<version>${mbr.version}</version>
172-
<scope>test</scope>
173-
</dependency>
174-
<dependency>
175-
<groupId>org.openjdk.jmh</groupId>
176-
<artifactId>jmh-core</artifactId>
177-
<version>${jmh.version}</version>
178-
<scope>test</scope>
179-
</dependency>
180170
<dependency>
181171
<groupId>org.openjdk.jmh</groupId>
182172
<artifactId>jmh-generator-annprocess</artifactId>
183-
<version>${jmh.version}</version>
184173
<scope>test</scope>
185174
</dependency>
186175
</dependencies>
187-
<build>
188-
<plugins>
189-
<plugin>
190-
<groupId>org.codehaus.mojo</groupId>
191-
<artifactId>build-helper-maven-plugin</artifactId>
192-
<version>3.3.0</version>
193-
<executions>
194-
<execution>
195-
<id>add-source</id>
196-
<phase>generate-sources</phase>
197-
<goals>
198-
<goal>add-test-source</goal>
199-
</goals>
200-
<configuration>
201-
<sources>
202-
<source>src/jmh/java</source>
203-
</sources>
204-
</configuration>
205-
</execution>
206-
</executions>
207-
</plugin>
208-
<plugin>
209-
<groupId>org.apache.maven.plugins</groupId>
210-
<artifactId>maven-surefire-plugin</artifactId>
211-
<configuration>
212-
<skip>true</skip>
213-
</configuration>
214-
</plugin>
215-
216-
<plugin>
217-
<groupId>org.apache.maven.plugins</groupId>
218-
<artifactId>maven-failsafe-plugin</artifactId>
219-
<configuration>
220-
<skip>true</skip>
221-
</configuration>
222-
</plugin>
223-
<plugin>
224-
<groupId>org.codehaus.mojo</groupId>
225-
<artifactId>exec-maven-plugin</artifactId>
226-
<version>3.1.0</version>
227-
<executions>
228-
<execution>
229-
<id>run-benchmarks</id>
230-
<phase>pre-integration-test</phase>
231-
<goals>
232-
<goal>exec</goal>
233-
</goals>
234-
<configuration>
235-
<classpathScope>test</classpathScope>
236-
<executable>java</executable>
237-
<arguments>
238-
<argument>-classpath</argument>
239-
<classpath/>
240-
<argument>org.openjdk.jmh.Main</argument>
241-
<argument>.*</argument>
242-
</arguments>
243-
</configuration>
244-
</execution>
245-
</executions>
246-
</plugin>
247-
</plugins>
248-
</build>
249176
<repositories>
250177
<repository>
251178
<id>jitpack.io</id>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2023-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.jdbc;
18+
19+
import java.util.concurrent.TimeUnit;
20+
21+
import org.openjdk.jmh.annotations.BenchmarkMode;
22+
import org.openjdk.jmh.annotations.Fork;
23+
import org.openjdk.jmh.annotations.Measurement;
24+
import org.openjdk.jmh.annotations.Mode;
25+
import org.openjdk.jmh.annotations.OutputTimeUnit;
26+
import org.openjdk.jmh.annotations.Warmup;
27+
28+
/**
29+
* Global benchmark settings.
30+
*
31+
* @author Mark Paluch
32+
*/
33+
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
34+
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
35+
@Fork(value = 1, warmups = 0)
36+
@BenchmarkMode(Mode.Throughput)
37+
@OutputTimeUnit(TimeUnit.SECONDS)
38+
public abstract class BenchmarkSettings {
39+
40+
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.concurrent.atomic.AtomicLong;
23+
24+
import javax.sql.DataSource;
25+
26+
import org.junit.platform.commons.annotation.Testable;
27+
import org.openjdk.jmh.annotations.Benchmark;
28+
import org.openjdk.jmh.annotations.Scope;
29+
import org.openjdk.jmh.annotations.Setup;
30+
import org.openjdk.jmh.annotations.State;
31+
import org.openjdk.jmh.annotations.TearDown;
32+
33+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
34+
import org.springframework.context.annotation.Bean;
35+
import org.springframework.context.annotation.Configuration;
36+
import org.springframework.data.annotation.Id;
37+
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
38+
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
39+
import org.springframework.data.relational.core.mapping.Embedded;
40+
import org.springframework.data.relational.core.mapping.Table;
41+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
42+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
43+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
44+
45+
/**
46+
* Benchmarks for Composite Ids in Spring Data JDBC.
47+
*
48+
* @author Mark Paluch
49+
*/
50+
@Testable
51+
public class CompositeIdBenchmarks extends BenchmarkSettings {
52+
53+
@Configuration
54+
static class BenchmarkConfiguration extends AbstractJdbcConfiguration {
55+
56+
@Bean
57+
NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {
58+
return new NamedParameterJdbcTemplate(dataSource);
59+
}
60+
61+
@Bean
62+
DataSource dataSource() {
63+
64+
return new EmbeddedDatabaseBuilder() //
65+
.generateUniqueName(true) //
66+
.setType(EmbeddedDatabaseType.HSQL) //
67+
.setScriptEncoding("UTF-8") //
68+
.ignoreFailedDrops(true) //
69+
.addScript(
70+
"classpath:/org.springframework.data.jdbc.core/CompositeIdAggregateTemplateHsqlIntegrationTests-hsql.sql") //
71+
.build();
72+
}
73+
}
74+
75+
@State(Scope.Benchmark)
76+
public static class BenchmarkState {
77+
78+
AnnotationConfigApplicationContext context;
79+
JdbcAggregateTemplate template;
80+
NamedParameterJdbcTemplate named;
81+
AtomicLong l = new AtomicLong();
82+
SimpleEntity alpha;
83+
84+
@Setup
85+
public void setup() {
86+
87+
context = new AnnotationConfigApplicationContext();
88+
context.register(BenchmarkConfiguration.class);
89+
context.refresh();
90+
context.start();
91+
92+
template = context.getBean(JdbcAggregateTemplate.class);
93+
named = context.getBean(NamedParameterJdbcTemplate.class);
94+
95+
alpha = template.insert(new SimpleEntity(new WrappedPk(l.incrementAndGet()), "alpha"));
96+
}
97+
98+
@TearDown
99+
public void cleanup() {
100+
context.close();
101+
}
102+
}
103+
104+
@Benchmark
105+
public Object namedTemplate(BenchmarkState state) {
106+
return state.named.query("SELECT * FROM SIMPLE_ENTITY WHERE id = :id", Map.of("id", state.alpha.wrappedPk.id),
107+
(rs, rowNum) -> 1);
108+
}
109+
110+
@Benchmark
111+
public Object jdbcTemplate(BenchmarkState state) {
112+
return state.named.getJdbcOperations().query("SELECT * FROM SIMPLE_ENTITY WHERE id = " + state.alpha.wrappedPk.id,
113+
(rs, rowNum) -> 1);
114+
}
115+
116+
@Benchmark
117+
public Object baselineInsert(BenchmarkState state) {
118+
return state.template.insert(new BaselineEntity(state.l.incrementAndGet(), "alpha"));
119+
}
120+
121+
@Benchmark
122+
public Object loadBaselineEntity(BenchmarkState state) {
123+
return state.template.findById(state.alpha.wrappedPk, BaselineEntity.class);
124+
}
125+
126+
@Benchmark
127+
public Object insert(BenchmarkState state) {
128+
return state.template.insert(new SimpleEntity(new WrappedPk(state.l.incrementAndGet()), "alpha"));
129+
}
130+
131+
@Benchmark
132+
public Object loadSimpleEntity(BenchmarkState state) {
133+
return state.template.findById(state.alpha.wrappedPk, SimpleEntity.class);
134+
}
135+
136+
@Benchmark
137+
public Object saveAndLoadEntityWithList(BenchmarkState state) {
138+
139+
WithList entity = state.template.insert(new WithList(new WrappedPk(state.l.incrementAndGet()), "alpha",
140+
List.of(new Child("Romulus"), new Child("Remus"))));
141+
142+
assertThat(entity.wrappedPk).isNotNull() //
143+
.extracting(WrappedPk::id).isNotNull();
144+
145+
return state.template.findById(entity.wrappedPk, WithList.class);
146+
}
147+
148+
@Benchmark
149+
public Object saveAndLoadSimpleEntityWithEmbeddedPk(BenchmarkState state) {
150+
151+
SimpleEntityWithEmbeddedPk entity = state.template
152+
.insert(new SimpleEntityWithEmbeddedPk(new EmbeddedPk(state.l.incrementAndGet(), "x"), "alpha"));
153+
154+
return state.template.findById(entity.embeddedPk, SimpleEntityWithEmbeddedPk.class);
155+
}
156+
157+
@Benchmark
158+
public void deleteSingleSimpleEntityWithEmbeddedPk(BenchmarkState state) {
159+
160+
List<SimpleEntityWithEmbeddedPk> entities = (List<SimpleEntityWithEmbeddedPk>) state.template
161+
.insertAll(List.of(new SimpleEntityWithEmbeddedPk(new EmbeddedPk(23L, "x"), "alpha")));
162+
163+
state.template.delete(entities.get(1));
164+
}
165+
166+
@Benchmark
167+
public void deleteMultipleSimpleEntityWithEmbeddedPk(BenchmarkState state) {
168+
169+
List<SimpleEntityWithEmbeddedPk> entities = (List<SimpleEntityWithEmbeddedPk>) state.template
170+
.insertAll(List.of(new SimpleEntityWithEmbeddedPk(new EmbeddedPk(23L, "x"), "alpha"),
171+
new SimpleEntityWithEmbeddedPk(new EmbeddedPk(23L, "y"), "beta")));
172+
173+
state.template.deleteAll(List.of(entities.get(1), entities.get(0)));
174+
}
175+
176+
@Benchmark
177+
public void updateSingleSimpleEntityWithEmbeddedPk(BenchmarkState state) {
178+
179+
List<SimpleEntityWithEmbeddedPk> entities = (List<SimpleEntityWithEmbeddedPk>) state.template
180+
.insertAll(List.of(new SimpleEntityWithEmbeddedPk(new EmbeddedPk(23L, "x"), "alpha"),
181+
new SimpleEntityWithEmbeddedPk(new EmbeddedPk(23L, "y"), "beta"),
182+
new SimpleEntityWithEmbeddedPk(new EmbeddedPk(24L, "y"), "gamma")));
183+
184+
SimpleEntityWithEmbeddedPk updated = new SimpleEntityWithEmbeddedPk(new EmbeddedPk(23L, "x"), "ALPHA");
185+
state.template.save(updated);
186+
187+
state.template.deleteAll(SimpleEntityWithEmbeddedPk.class);
188+
}
189+
190+
private record WrappedPk(Long id) {
191+
}
192+
193+
@Table("SIMPLE_ENTITY")
194+
private record BaselineEntity( //
195+
@Id Long id, //
196+
String name //
197+
) {
198+
}
199+
200+
private record SimpleEntity( //
201+
@Id @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL) WrappedPk wrappedPk, //
202+
String name //
203+
) {
204+
}
205+
206+
private record Child(String name) {
207+
}
208+
209+
private record WithList( //
210+
@Id @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL) WrappedPk wrappedPk, //
211+
String name, List<Child> children) {
212+
}
213+
214+
private record EmbeddedPk(Long one, String two) {
215+
}
216+
217+
private record SimpleEntityWithEmbeddedPk( //
218+
@Id @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL) EmbeddedPk embeddedPk, //
219+
String name //
220+
) {
221+
}
222+
223+
private record SingleReference( //
224+
@Id @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL) EmbeddedPk embeddedPk, //
225+
String name, //
226+
Child child) {
227+
}
228+
229+
private record WithListAndCompositeId( //
230+
@Id @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL) EmbeddedPk embeddedPk, //
231+
String name, //
232+
List<Child> child) {
233+
}
234+
235+
}

0 commit comments

Comments
 (0)