Skip to content

Commit 993ffe7

Browse files
committed
Move Benchmarks from performance module into spring-data-jpa.
Closes #3655
1 parent d955b78 commit 993ffe7

File tree

9 files changed

+624
-0
lines changed

9 files changed

+624
-0
lines changed

spring-data-jpa/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,16 @@
351351
<artifactId>hibernate-jpamodelgen</artifactId>
352352
<version>${hibernate}</version>
353353
</path>
354+
<path>
355+
<groupId>org.hibernate.orm</groupId>
356+
<artifactId>hibernate-core</artifactId>
357+
<version>${hibernate}</version>
358+
</path>
359+
<path>
360+
<groupId>org.openjdk.jmh</groupId>
361+
<artifactId>jmh-generator-annprocess</artifactId>
362+
<version>${jmh}</version>
363+
</path>
354364
<path>
355365
<groupId>jakarta.persistence</groupId>
356366
<artifactId>jakarta.persistence-api</artifactId>
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* Copyright 2024 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.jpa.benchmark;
17+
18+
import jakarta.persistence.EntityManager;
19+
import jakarta.persistence.EntityManagerFactory;
20+
import jakarta.persistence.Query;
21+
import jakarta.persistence.TypedQuery;
22+
import jakarta.persistence.criteria.CriteriaBuilder;
23+
import jakarta.persistence.criteria.CriteriaQuery;
24+
import jakarta.persistence.criteria.Root;
25+
26+
import java.util.List;
27+
import java.util.Properties;
28+
import java.util.Set;
29+
30+
import org.hibernate.jpa.HibernatePersistenceProvider;
31+
import org.junit.platform.commons.annotation.Testable;
32+
import org.openjdk.jmh.annotations.Benchmark;
33+
import org.openjdk.jmh.annotations.Fork;
34+
import org.openjdk.jmh.annotations.Level;
35+
import org.openjdk.jmh.annotations.Measurement;
36+
import org.openjdk.jmh.annotations.Scope;
37+
import org.openjdk.jmh.annotations.Setup;
38+
import org.openjdk.jmh.annotations.State;
39+
import org.openjdk.jmh.annotations.TearDown;
40+
import org.openjdk.jmh.annotations.Timeout;
41+
import org.openjdk.jmh.annotations.Warmup;
42+
43+
import org.springframework.data.domain.Sort;
44+
import org.springframework.data.jpa.benchmark.model.IPersonProjection;
45+
import org.springframework.data.jpa.benchmark.model.Person;
46+
import org.springframework.data.jpa.benchmark.model.Profile;
47+
import org.springframework.data.jpa.benchmark.repository.PersonRepository;
48+
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
49+
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
50+
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
51+
import org.springframework.util.ObjectUtils;
52+
53+
/**
54+
* @author Christoph Strobl
55+
*/
56+
@Testable
57+
@Fork(1)
58+
@Warmup(time = 2, iterations = 3)
59+
@Measurement(time = 2)
60+
@Timeout(time = 2)
61+
public class RepositoryFinderBenchmarks {
62+
63+
private static final String PERSON_FIRSTNAME = "first";
64+
private static final String COLUMN_PERSON_FIRSTNAME = "firstname";
65+
66+
@State(Scope.Benchmark)
67+
public static class BenchmarkParameters {
68+
69+
EntityManager entityManager;
70+
PersonRepository repositoryProxy;
71+
72+
@Setup(Level.Iteration)
73+
public void doSetup() {
74+
75+
createEntityManager();
76+
77+
if (!entityManager.getTransaction().isActive()) {
78+
79+
if (ObjectUtils.nullSafeEquals(
80+
entityManager.createNativeQuery("SELECT COUNT(*) FROM person", Integer.class).getSingleResult(),
81+
Integer.valueOf(0))) {
82+
83+
entityManager.getTransaction().begin();
84+
85+
Profile generalProfile = new Profile("general");
86+
Profile sdUserProfile = new Profile("sd-user");
87+
88+
entityManager.persist(generalProfile);
89+
entityManager.persist(sdUserProfile);
90+
91+
Person person = new Person(PERSON_FIRSTNAME, "last");
92+
person.setProfiles(Set.of(generalProfile, sdUserProfile));
93+
entityManager.persist(person);
94+
entityManager.getTransaction().commit();
95+
}
96+
}
97+
98+
this.repositoryProxy = createRepository();
99+
}
100+
101+
@TearDown(Level.Iteration)
102+
public void doTearDown() {
103+
entityManager.close();
104+
}
105+
106+
private void createEntityManager() {
107+
108+
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
109+
factoryBean.setPersistenceUnitName("benchmark");
110+
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
111+
factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
112+
factoryBean.setPersistenceXmlLocation("classpath*:META-INF/persistence-jmh.xml");
113+
factoryBean.setMappingResources("classpath*:META-INF/orm-jmh.xml");
114+
115+
Properties properties = new Properties();
116+
properties.put("jakarta.persistence.jdbc.url", "jdbc:h2:mem:test");
117+
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
118+
properties.put("hibernate.hbm2ddl.auto", "update");
119+
properties.put("hibernate.xml_mapping_enabled", "false");
120+
121+
factoryBean.setJpaProperties(properties);
122+
factoryBean.afterPropertiesSet();
123+
124+
EntityManagerFactory entityManagerFactory = factoryBean.getObject();
125+
entityManager = entityManagerFactory.createEntityManager();
126+
}
127+
128+
PersonRepository createRepository() {
129+
JpaRepositoryFactory repositoryFactory = new JpaRepositoryFactory(entityManager);
130+
return repositoryFactory.getRepository(PersonRepository.class);
131+
}
132+
}
133+
134+
@Benchmark
135+
public PersonRepository repositoryBootstrap(BenchmarkParameters parameters) {
136+
return parameters.createRepository();
137+
}
138+
139+
@Benchmark
140+
public List<Person> baselineEntityManagerCriteriaQuery(BenchmarkParameters parameters) {
141+
142+
CriteriaBuilder criteriaBuilder = parameters.entityManager.getCriteriaBuilder();
143+
CriteriaQuery<Person> query = criteriaBuilder.createQuery(Person.class);
144+
Root<Person> root = query.from(Person.class);
145+
TypedQuery<Person> typedQuery = parameters.entityManager
146+
.createQuery(query.where(criteriaBuilder.equal(root.get(COLUMN_PERSON_FIRSTNAME), PERSON_FIRSTNAME)));
147+
148+
return typedQuery.getResultList();
149+
}
150+
151+
@Benchmark
152+
public List<Person> baselineEntityManagerHQLQuery(BenchmarkParameters parameters) {
153+
154+
Query query = parameters.entityManager
155+
.createQuery("SELECT p FROM org.springframework.data.jpa.benchmark.model.Person p WHERE p.firstname = ?1");
156+
query.setParameter(1, PERSON_FIRSTNAME);
157+
158+
return query.getResultList();
159+
}
160+
161+
@Benchmark
162+
public Long baselineEntityManagerCount(BenchmarkParameters parameters) {
163+
164+
Query query = parameters.entityManager.createQuery(
165+
"SELECT COUNT(*) FROM org.springframework.data.jpa.benchmark.model.Person p WHERE p.firstname = ?1");
166+
query.setParameter(1, PERSON_FIRSTNAME);
167+
168+
return (Long) query.getSingleResult();
169+
}
170+
171+
@Benchmark
172+
public List<Person> derivedFinderMethod(BenchmarkParameters parameters) {
173+
return parameters.repositoryProxy.findAllByFirstname(PERSON_FIRSTNAME);
174+
}
175+
176+
@Benchmark
177+
public List<IPersonProjection> derivedFinderMethodWithInterfaceProjection(BenchmarkParameters parameters) {
178+
return parameters.repositoryProxy.findAllAndProjectToInterfaceByFirstname(PERSON_FIRSTNAME);
179+
}
180+
181+
@Benchmark
182+
public List<Person> stringBasedQuery(BenchmarkParameters parameters) {
183+
return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME);
184+
}
185+
186+
@Benchmark
187+
public List<Person> stringBasedQueryDynamicSort(BenchmarkParameters parameters) {
188+
return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME, Sort.by(COLUMN_PERSON_FIRSTNAME));
189+
}
190+
191+
@Benchmark
192+
public List<Person> stringBasedNativeQuery(BenchmarkParameters parameters) {
193+
return parameters.repositoryProxy.findAllWithNativeQueryByFirstname(PERSON_FIRSTNAME);
194+
}
195+
196+
@Benchmark
197+
public Long derivedCount(BenchmarkParameters parameters) {
198+
return parameters.repositoryProxy.countByFirstname(PERSON_FIRSTNAME);
199+
}
200+
201+
@Benchmark
202+
public Long stringBasedCount(BenchmarkParameters parameters) {
203+
return parameters.repositoryProxy.countWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME);
204+
}
205+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2024 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.jpa.benchmark.model;
17+
18+
/**
19+
* @author Christoph Strobl
20+
*/
21+
public interface IPersonProjection {
22+
23+
String getFirstname();
24+
String getLastname();
25+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2024 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.jpa.benchmark.model;
17+
18+
import jakarta.persistence.Column;
19+
import jakarta.persistence.Entity;
20+
import jakarta.persistence.GeneratedValue;
21+
import jakarta.persistence.GenerationType;
22+
import jakarta.persistence.Id;
23+
import jakarta.persistence.ManyToMany;
24+
import jakarta.persistence.Table;
25+
26+
import java.util.Set;
27+
28+
/**
29+
* @author Christoph Strobl
30+
*/
31+
@Entity
32+
@Table(name = "person")
33+
public class Person {
34+
35+
@Id
36+
@GeneratedValue(strategy = GenerationType.AUTO) //
37+
private Integer id;
38+
39+
private String firstname;
40+
private String lastname;
41+
42+
private int age;
43+
private boolean decased;
44+
45+
@Column(nullable = false, unique = true) //
46+
private String emailAddress;
47+
48+
@ManyToMany //
49+
private Set<Profile> profiles;
50+
51+
public Person() {}
52+
53+
public Person(String firstname, String lastname) {
54+
this(firstname, lastname, "%s.%[email protected]");
55+
}
56+
57+
public Person(String firstname, String lastname, String emailAddress) {
58+
59+
this.firstname = firstname;
60+
this.lastname = lastname;
61+
this.emailAddress = emailAddress;
62+
}
63+
64+
public Integer getId() {
65+
return id;
66+
}
67+
68+
public void setId(Integer id) {
69+
this.id = id;
70+
}
71+
72+
public String getEmailAddress() {
73+
return emailAddress;
74+
}
75+
76+
public void setEmailAddress(String emailAddress) {
77+
this.emailAddress = emailAddress;
78+
}
79+
80+
public String getFirstname() {
81+
return firstname;
82+
}
83+
84+
public void setFirstname(String firstname) {
85+
this.firstname = firstname;
86+
}
87+
88+
public String getLastname() {
89+
return lastname;
90+
}
91+
92+
public void setLastname(String lastname) {
93+
this.lastname = lastname;
94+
}
95+
96+
public int getAge() {
97+
return age;
98+
}
99+
100+
public void setAge(int age) {
101+
this.age = age;
102+
}
103+
104+
public boolean isDecased() {
105+
return decased;
106+
}
107+
108+
public void setDecased(boolean decased) {
109+
this.decased = decased;
110+
}
111+
112+
public Set<Profile> getProfiles() {
113+
return profiles;
114+
}
115+
116+
public void setProfiles(Set<Profile> profiles) {
117+
this.profiles = profiles;
118+
}
119+
}

0 commit comments

Comments
 (0)