#123 - Benchmark overhaul.

Unified benchmarks into one for JDBC and JPA. They're now both testing the data access with the plain infrastructure (JdbcTemplate and EntityManager) plus interactions via Spring Data repositories on top of that. Added build profiles to run the same benchmarks against an in-memory H2, a locally running H2 and a locally running Postgres. See the readme for setup instructions.

The JdbcFixture contains code to optionally disable event publication for the JDBC operation. Uncomment the line invoking disableEntityCallbacks(…).

Changed the benchmarks to use 10 warmup iterations instead of 5 to make sure we're at stable numbers once the measurement happens.
This commit is contained in:
Oliver Drotbohm
2019-08-13 09:59:54 +02:00
parent 231ba479ca
commit 757318b5cd
21 changed files with 382 additions and 268 deletions

View File

@@ -36,6 +36,11 @@
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

View File

@@ -0,0 +1,35 @@
= Benchmarks for relational data access with Spring Data
This benchmark evaluates various options of relation data access in the Spring Data project family:
- JDBC and Spring Data JDBC
- JPA and Spring Data JPA
The primary purpose of the benchmark is to help the team detect degradations in performance quickly or verify optimizations made in various areas of the libraries.
It also helps justifying differences between numbers in rather clean room contexts (an embedded database) and scenarios that use a more realistic setup like a locally running database.
That difference alone will help reasoning about the real-world impact of an optimization or degradation.
== Benchmark model
The benchmarks use a very simple model of a book with a title and pages attribute.
We deliberately chose a simple model as the benchmarks are supposed to measure the overhead the Spring Data mapping and repository infrastructure adds on top of the raw JDBC and JPA alternatives.
There are two major benchmark operations:
1. Finding all books (8 items)
2. Finding a single book by title.
There are different flavors of those operations to measure the impact of different setups to execute them:
- the effect of read-only transactions in the find all case
- the difference between derived and declared queries in JPA
== Infrastructure
The benchmarks are run against the following databases:
- In-memory H2
- A locally running H2 (port 9092, database name `benchmark`, user `sa`, empty password)
- A locally running Postgres (port 5432, database name `benchmark`, no user, no password)
The settings can be adapted by tweaking corresponding `application-$database.properties` file in `src/main/resources`.

View File

@@ -1,24 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.microbenchmark;
/**
* @author Oliver Drotbohm
*/
public class Constants {
public static final int NUMBER_OF_BOOKS = 8;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.microbenchmark;
import java.util.Arrays;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import lombok.experimental.UtilityClass;
@UtilityClass
public class FixtureUtils {
public static final int NUMBER_OF_BOOKS = 8;
public static ConfigurableApplicationContext createContext(Class<?> configuration, String api, String database) {
SpringApplication application = new SpringApplication();
application.addPrimarySources(Collections.singletonList(configuration));
application.setLazyInitialization(true);
application.setAdditionalProfiles(api, database);
System.out.println("Activating profiles: " + Arrays.asList(api, database).toString());
return application.run();
}
}

View File

@@ -15,50 +15,127 @@
*/
package org.springframework.data.microbenchmark.jdbc;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeSet;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.util.Optional;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.jdbc.core.convert.EntityRowMapper;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.microbenchmark.common.AbstractMicrobenchmark;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.RowMapper;
import com.mockrunner.mock.jdbc.MockResultSet;
/**
* Benchmark for JDBC and Spring Data JDBC
*
* @author Oliver Drotbohm
*/
public class JdbcBenchmark extends AbstractMicrobenchmark {
private JdbcFixture fixture;
private static final String BY_TITLE_SQL = "SELECT id, title, pages FROM Book where title = ?";
@Param({ "postgres", "h2-in-memory", "h2" }) String profile;
JdbcOperations operations;
RowMapper<Book> bookMapper;
EntityRowMapper<Book> bookEntityMapper;
JdbcBookRepository repository;
Set<String> columns;
HashMap<String, Object> values;
@Setup
@SuppressWarnings("unchecked")
public void setUp() {
this.fixture = new JdbcFixture();
JdbcFixture fixture = new JdbcFixture(profile);
this.bookMapper = fixture.getBookMapper();
ConfigurableApplicationContext context = fixture.getContext();
this.operations = context.getBean(JdbcOperations.class);
this.repository = context.getBean(JdbcBookRepository.class);
JdbcConverter converter = context.getBean(JdbcConverter.class);
JdbcMappingContext mappingContext = context.getBean(JdbcMappingContext.class);
RelationalPersistentEntity<Book> requiredPersistentEntity = (RelationalPersistentEntity<Book>) mappingContext
.getRequiredPersistentEntity(Book.class);
this.bookEntityMapper = new EntityRowMapper<Book>(requiredPersistentEntity, converter);
// ResultSet mock
this.columns = new TreeSet<>();
columns.add("id");
columns.add("title");
columns.add("pages");
this.values = new HashMap<>();
values.put("id", 1L);
values.put("title", "title0");
values.put("pages", 42L);
}
@TearDown
public void tearDown() {
fixture.close();
@Benchmark
public void convertWithSpringData(Blackhole sink) throws Exception {
MockResultSet resultSet = new MockResultSet("book");
resultSet.addColumns(columns);
resultSet.addRow(values);
resultSet.next();
sink.consume(this.bookEntityMapper.mapRow(resultSet, 1));
}
@Benchmark
public void findByTitle(Blackhole sink) {
Book book = fixture.getOperations().queryForObject("SELECT id, title, pages FROM Book where title = ?1",
new Object[] { "title0" }, fixture.getBookMapper());
sink.consume(book);
sink.consume(operations.queryForObject(BY_TITLE_SQL, new Object[] { "title0" }, bookMapper));
}
@Benchmark
public void findByTitleOptional(Blackhole sink) {
Book book = fixture.getOperations().queryForObject("SELECT id, title, pages FROM Book where title = ?1",
new Object[] { "title0" }, fixture.getBookMapper());
sink.consume(Optional.of(book));
sink.consume(Optional.of(operations.queryForObject(BY_TITLE_SQL, new Object[] { "title0" }, bookMapper)));
}
@Benchmark
public void findAll(Blackhole sink) {
sink.consume(fixture.getOperations().query("SELECT id, title, pages FROM Book", fixture.getBookMapper()));
sink.consume(operations.query("SELECT id, title, pages FROM Book", bookMapper));
}
@Benchmark
public void findAllWithSpringDataConversion(Blackhole sink) {
sink.consume(operations.query("SELECT id, title, pages FROM Book", bookEntityMapper));
}
@Benchmark
public void repositoryFindByTitle(Blackhole sink) {
sink.consume(repository.findByTitle("title0"));
}
@Benchmark
public void repositoryFindTransactionalByTitle(Blackhole sink) {
sink.consume(repository.findTransactionalByTitle("title0"));
}
@Benchmark
public void repositoryFindByTitleOptional(Blackhole sink) {
sink.consume(repository.findOptionalByTitle("title0"));
}
@Benchmark
public void repositoryFindAll(Blackhole sink) {
sink.consume(repository.findAll());
}
}

View File

@@ -13,22 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.microbenchmark.jdbc.springdata;
package org.springframework.data.microbenchmark.jdbc;
import java.util.Optional;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.microbenchmark.jdbc.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* A repository for {@link Book} instances.
*
* @author Oliver Drotbohm
*/
public interface JdbcBookRepository extends CrudRepository<Book, Long> {
@Query("SELECT id, title, pages FROM Book where title = :title")
interface JdbcBookRepository extends CrudRepository<Book, Long> {
static final String BY_TITLE = "SELECT id, title, pages FROM Book where title = :title";
@Transactional(propagation = Propagation.NOT_SUPPORTED)
Iterable<Book> findAll();
@Query(BY_TITLE)
Book findByTitle(String title);
@Transactional(readOnly = true)
@Query(BY_TITLE)
Book findTransactionalByTitle(String title);
@Query("SELECT id, title, pages FROM Book where title = :title")
@Query(BY_TITLE)
Optional<Book> findOptionalByTitle(String title);
}

View File

@@ -15,60 +15,86 @@
*/
package org.springframework.data.microbenchmark.jdbc;
import java.lang.reflect.Field;
import org.springframework.aop.framework.Advised;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.microbenchmark.FixtureUtils;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.ReflectionUtils;
import lombok.Getter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.microbenchmark.Constants;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.RowMapper;
/**
* Test fixture for JDBC and Spring Data JDBC benchmarks.
*
* @author Oliver Drotbohm
*/
public class JdbcFixture {
private final @Getter JdbcOperations operations;
private final @Getter RowMapper<Book> bookMapper;
private final List<Book> books = new ArrayList<>();
class JdbcFixture {
private final @Getter ConfigurableApplicationContext context;
private final @Getter RowMapper<Book> bookMapper;
public JdbcFixture() {
JdbcFixture(String database) {
SpringApplication application = new SpringApplication();
application.addPrimarySources(Collections.singletonList(JdbcApplication.class));
application.setLazyInitialization(true);
application.setAdditionalProfiles("jdbc");
this.context = application.run();
this.context = FixtureUtils.createContext(JdbcApplication.class, "jdbc", database);
this.operations = context.getBean(JdbcOperations.class);
// Schema
this.operations.execute(
"create table Book (id bigint not null auto_increment, title varchar(255), pages integer not null, primary key (id))");
// Data
IntStream.range(0, Constants.NUMBER_OF_BOOKS) //
.mapToObj(it -> new Book(null, "title" + it, it)) //
.peek(it -> books.add(it)) //
.forEach(it -> operations.update("INSERT INTO Book VALUES (null, ?1, ?2)", it.getTitle(), it.getPages()));
// disableEntityCallbacks(context);
this.bookMapper = (rs, rowNum) -> new Book(rs.getLong("id"), rs.getString("title"), rs.getInt("pages"));
}
private static void disableEntityCallbacks(ApplicationContext context) {
JdbcBookRepository repository = context.getBean(JdbcBookRepository.class);
Field field = ReflectionUtils.findField(SimpleJdbcRepository.class, "entityOperations");
ReflectionUtils.makeAccessible(field);
try {
JdbcAggregateTemplate aggregateTemplate = (JdbcAggregateTemplate) ReflectionUtils.getField(field,
((Advised) repository).getTargetSource().getTarget());
field = ReflectionUtils.findField(JdbcAggregateTemplate.class, "publisher");
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, aggregateTemplate, NoOpApplicationEventPublisher.INSTANCE);
aggregateTemplate.setEntityCallbacks(NoOpEntityCallbacks.INSTANCE);
} catch (Exception o_O) {
throw new RuntimeException(o_O);
}
}
@SpringBootApplication
static class JdbcApplication {
static class JdbcApplication {}
enum NoOpApplicationEventPublisher implements ApplicationEventPublisher {
INSTANCE;
@Override
public void publishEvent(Object event) {}
}
enum NoOpEntityCallbacks implements EntityCallbacks {
public void close() {
context.close();
INSTANCE;
@Override
public void addEntityCallback(EntityCallback<?> callback) {}
@Override
@SuppressWarnings("rawtypes")
public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Object... args) {
return entity;
}
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.microbenchmark.jdbc.springdata;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.infra.Blackhole;
import org.springframework.data.microbenchmark.common.AbstractMicrobenchmark;
import org.springframework.data.microbenchmark.jdbc.JdbcFixture;
/**
* @author Oliver Drotbohm
*/
public class SpringDataJdbcBenchmark extends AbstractMicrobenchmark {
private JdbcBookRepository repository;
@Setup
public void setUp() {
JdbcFixture fixture = new JdbcFixture();
this.repository = fixture.getContext().getBean(JdbcBookRepository.class);
}
@Benchmark
public void findByTitle(Blackhole sink) {
sink.consume(repository.findByTitle("title0"));
}
@Benchmark
public void findByTitleOptional(Blackhole sink) {
sink.consume(repository.findOptionalByTitle("title0"));
}
@Benchmark
public void findAll(Blackhole sink) {
sink.consume(repository.findAll());
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.microbenchmark.jpa;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
@@ -25,69 +26,99 @@ import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.infra.Blackhole;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.microbenchmark.common.AbstractMicrobenchmark;
/**
* Benchmarks for JPA and Spring Data JPA.
*
* @author Oliver Drotbohm
*/
public class JpaBenchmark extends AbstractMicrobenchmark {
private JpaFixture fixture;
@Param({ "postgres", "h2-in-memory", "h2" })
String profile;
EntityManager em;
JpaBookRepository repository;
@Setup
public void setUp() {
this.fixture = new JpaFixture();
ConfigurableApplicationContext context = new JpaFixture(profile).getContext();
this.em = context.getBean(EntityManager.class);
this.repository = context.getBean(JpaBookRepository.class);
}
@Benchmark
public void findByTitle(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
Query query = em.createQuery("select b from Book b where b.title = ?1");
query.setParameter(1, "title0");
Query query = em.createQuery("select b from Book b where b.title = ?1");
query.setParameter(1, "title0");
sink.consume(query.getSingleResult());
});
sink.consume(query.getSingleResult());
}
@Benchmark
public void findByTitleCriteria(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> q = cb.createQuery(Book.class);
Root<Book> c = q.from(Book.class);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> q = cb.createQuery(Book.class);
Root<Book> c = q.from(Book.class);
ParameterExpression<String> parameter = cb.parameter(String.class);
ParameterExpression<String> parameter = cb.parameter(String.class);
TypedQuery<Book> query = em.createQuery(q.select(c).where(cb.equal(c.get("title"), parameter)));
query.setParameter(parameter, "title0");
TypedQuery<Book> query = em.createQuery(q.select(c).where(cb.equal(c.get("title"), parameter)));
query.setParameter(parameter, "title0");
sink.consume(query.getSingleResult());
});
sink.consume(query.getSingleResult());
}
@Benchmark
public void findByTitleOptional(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
Query query = em.createQuery("select b from Book b where b.title = ?1");
query.setParameter(1, "title0");
Query query = em.createQuery("select b from Book b where b.title = ?1");
query.setParameter(1, "title0");
sink.consume(Optional.of(query.getSingleResult()));
});
sink.consume(Optional.of(query.getSingleResult()));
}
@Benchmark
public void findAll(Blackhole sink) {
sink.consume(em.createQuery("select b from Book b").getResultList());
}
fixture.withNonTransactionalEntityManager(em -> {
sink.consume(em.createQuery("select b from Book b").getResultList());
});
@Benchmark
public void repositoryFindByTitle(Blackhole sink) {
sink.consume(repository.findDerivedByTitle("title0"));
}
@Benchmark
public void repositoryFindTransactionalByTitle(Blackhole sink) {
sink.consume(repository.findTransactionalDerivedByTitle("title0"));
}
@Benchmark
public void repositoryFindByTitleDeclared(Blackhole sink) {
sink.consume(repository.findDeclaredByTitle("title0"));
}
@Benchmark
public void repositoryFindTransactionalByTitleDeclared(Blackhole sink) {
sink.consume(repository.findTransactionalDeclaredByTitle("title0"));
}
@Benchmark
public void repositoryFindByTitleOptional(Blackhole sink) {
sink.consume(repository.findOptionalDerivedByTitle("title0"));
}
@Benchmark
public void repositoryFindAll(Blackhole sink) {
sink.consume(repository.findAll());
}
}

View File

@@ -13,23 +13,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.microbenchmark.jpa.springdata;
package org.springframework.data.microbenchmark.jpa;
import java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.microbenchmark.jpa.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Oliver Drotbohm
*/
public interface JpaBookRepository extends CrudRepository<Book, Long> {
interface JpaBookRepository extends CrudRepository<Book, Long> {
static final String BY_TITLE_JPQL = "select b from Book b where b.title = :title";
@Query("select b from Book b where b.title = :title")
@Query(BY_TITLE_JPQL)
Book findDeclaredByTitle(String title);
@Transactional(readOnly = true)
@Query(BY_TITLE_JPQL)
Book findTransactionalDeclaredByTitle(String title);
Book findDerivedByTitle(String title);
@Transactional(readOnly = true)
Book findTransactionalDerivedByTitle(String title);
Optional<Book> findOptionalDerivedByTitle(String title);
}

View File

@@ -17,45 +17,40 @@ package org.springframework.data.microbenchmark.jpa;
import lombok.Getter;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.stream.IntStream;
import javax.persistence.EntityManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.microbenchmark.Constants;
import org.springframework.data.microbenchmark.FixtureUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
/**
* Test fixture for JPA and Spring Data JPA benchmarks.
*
* @author Oliver Drotbohm
*/
public class JpaFixture {
class JpaFixture {
private final @Getter ConfigurableApplicationContext context;
public JpaFixture() {
SpringApplication application = new SpringApplication();
application.addPrimarySources(Collections.singletonList(JpaApplication.class));
application.setAdditionalProfiles("jpa");
application.setLazyInitialization(true);
this.context = application.run();
JpaFixture(String database) {
this.context = FixtureUtils.createContext(JpaApplication.class, "jpa", database);
withTransactionalEntityManager(em -> {
IntStream.range(0, Constants.NUMBER_OF_BOOKS) //
IntStream.range(0, FixtureUtils.NUMBER_OF_BOOKS) //
.mapToObj(it -> new Book(null, "title" + it, it)) //
.forEach(em::persist);
});
}
public void withTransactionalEntityManager(Consumer<EntityManager> consumer) {
private void withTransactionalEntityManager(Consumer<EntityManager> consumer) {
PlatformTransactionManager manager = context.getBean(PlatformTransactionManager.class);
TransactionStatus status = manager.getTransaction(new DefaultTransactionDefinition());
@@ -69,15 +64,6 @@ public class JpaFixture {
em.close();
}
public void withNonTransactionalEntityManager(Consumer<EntityManager> consumer) {
EntityManager em = context.getBean(EntityManager.class);
consumer.accept(em);
em.close();
}
@SpringBootApplication
static class JpaApplication {}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.microbenchmark.jpa.springdata;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.infra.Blackhole;
import org.springframework.data.microbenchmark.common.AbstractMicrobenchmark;
import org.springframework.data.microbenchmark.jpa.JpaFixture;
/**
* @author Oliver Drotbohm
*/
public class SpringDataJpaBenchmark extends AbstractMicrobenchmark {
private JpaFixture fixture;
private JpaBookRepository repository;
@Setup
public void setUp() {
this.fixture = new JpaFixture();
this.repository = fixture.getContext().getBean(JpaBookRepository.class);
}
@Benchmark
public void findByTitle(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
sink.consume(repository.findDerivedByTitle("title0"));
});
}
@Benchmark
public void findByTitleDeclared(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
sink.consume(repository.findDeclaredByTitle("title0"));
});
}
@Benchmark
public void findByTitleOptional(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
sink.consume(repository.findOptionalDerivedByTitle("title0"));
});
}
@Benchmark
public void findAll(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
sink.consume(repository.findAll());
});
}
}

View File

@@ -0,0 +1,2 @@
spring.datasource.platform=h2

View File

@@ -0,0 +1,6 @@
spring.datasource.url=jdbc:h2:tcp://localhost:9092/~/benchmark
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.platform=h2
spring.datasource.initialization-mode=always

View File

@@ -1,2 +1,2 @@
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.ddl-auto=create-drop
spring.data.jdbc.repositories.enabled=false

View File

@@ -0,0 +1,4 @@
spring.datasource.url=jdbc:postgresql://localhost:5432/benchmark
spring.datasource.platform=postgres
spring.datasource.initialization-mode=always

View File

@@ -0,0 +1,8 @@
INSERT INTO Book VALUES (null, 'title0', 0);
INSERT INTO Book VALUES (null, 'title1', 1);
INSERT INTO Book VALUES (null, 'title2', 2);
INSERT INTO Book VALUES (null, 'title3', 3);
INSERT INTO Book VALUES (null, 'title4', 4);
INSERT INTO Book VALUES (null, 'title5', 5);
INSERT INTO Book VALUES (null, 'title6', 6);
INSERT INTO Book VALUES (null, 'title7', 7);

View File

@@ -0,0 +1,8 @@
INSERT INTO Book(title, pages) VALUES ('title0', 0);
INSERT INTO Book(title, pages) VALUES ('title1', 1);
INSERT INTO Book(title, pages) VALUES ('title2', 2);
INSERT INTO Book(title, pages) VALUES ('title3', 3);
INSERT INTO Book(title, pages) VALUES ('title4', 4);
INSERT INTO Book(title, pages) VALUES ('title5', 5);
INSERT INTO Book(title, pages) VALUES ('title6', 6);
INSERT INTO Book(title, pages) VALUES ('title7', 7);

View File

@@ -0,0 +1,7 @@
drop table Book if exists;
create table Book (
id bigint not null auto_increment,
title varchar(255),
pages integer not null,
primary key (id)
);

View File

@@ -0,0 +1,6 @@
drop table if exists Book;
create table Book (
id serial primary key,
title varchar(255),
pages integer not null
);

View File

@@ -31,10 +31,10 @@ import org.openjdk.jmh.annotations.Warmup;
* @author Mark Paluch
* @see Microbenchmark
*/
@Warmup(iterations = 5)
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@Fork(value = 1, jvmArgs = { "-server", "-XX:+HeapDumpOnOutOfMemoryError", "-Xms1024m", "-Xmx1024m",
"-XX:MaxDirectMemorySize=1024m" })
"-XX:MaxDirectMemorySize=1024m", "-noverify" })
@State(Scope.Thread)
@RunWith(Microbenchmark.class)
public abstract class AbstractMicrobenchmark {