#123 - Initial draft of benchmarks for relational data access (JDBC & JPA).

This commit is contained in:
Oliver Drotbohm
2019-07-22 16:29:43 +02:00
parent ba87acef37
commit 63843d6d59
18 changed files with 711 additions and 18 deletions

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<groupId>org.springframework.data.benchmark</groupId>
<artifactId>spring-data-benchmark-parent</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
@@ -22,7 +22,7 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<groupId>org.springframework.data.benchmark</groupId>
<artifactId>spring-data-benchmark-parent</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
@@ -22,21 +22,13 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.28.2</version>
</dependency>
<dependency>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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">
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId>
<groupId>org.springframework.data.benchmark</groupId>
<artifactId>spring-data-benchmark-parent</artifactId>
<packaging>pom</packaging>
@@ -21,18 +22,20 @@
<module>support</module>
<module>commons</module>
<module>mongodb</module>
<module>relational</module>
</modules>
<properties>
<releasetrain.version>Moore-BUILD-SNAPSHOT</releasetrain.version>
<jmh.version>1.19</jmh.version>
<spring-boot.version>2.2.0.BUILD-SNAPSHOT</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-releasetrain</artifactId>
<version>${releasetrain.version}</version>
<type>pom</type>
@@ -83,6 +86,17 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<parameters>true</parameters>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data.benchmark</groupId>
<artifactId>spring-data-benchmark-parent</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-data-benchmark-relational</artifactId>
<name>Spring Data Benchmarks - Relational Microbenchmarks</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-data-benchmark-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.2.0.DATACMNS-1556-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.0.DATAJPA-1575-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jdbc</artifactId>
<version>1.1.0.DATAJDBC-399-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational</artifactId>
<version>1.1.0.DATAJDBC-399-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito}</version>
</dependency>
<dependency>
<groupId>com.mockrunner</groupId>
<artifactId>mockrunner-jdbc</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,24 @@
/*
* 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,33 @@
/*
* 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;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.data.annotation.Id;
/**
* @author Oliver Drotbohm
*/
@Data
@AllArgsConstructor
public class Book {
private @Id Long id;
private String title;
private int pages;
}

View File

@@ -0,0 +1,64 @@
/*
* 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;
import org.openjdk.jmh.annotations.Benchmark;
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.data.microbenchmark.common.AbstractMicrobenchmark;
/**
* @author Oliver Drotbohm
*/
public class JdbcBenchmark extends AbstractMicrobenchmark {
private JdbcFixture fixture;
@Setup
public void setUp() {
this.fixture = new JdbcFixture();
}
@TearDown
public void tearDown() {
fixture.close();
}
@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);
}
@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));
}
@Benchmark
public void findAll(Blackhole sink) {
sink.consume(fixture.getOperations().query("SELECT id, title, pages FROM Book", fixture.getBookMapper()));
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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;
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;
/**
* @author Oliver Drotbohm
*/
public class JdbcFixture {
private final @Getter JdbcOperations operations;
private final @Getter RowMapper<Book> bookMapper;
private final List<Book> books = new ArrayList<>();
private final @Getter ConfigurableApplicationContext context;
public JdbcFixture() {
SpringApplication application = new SpringApplication();
application.addPrimarySources(Collections.singletonList(JdbcApplication.class));
application.setLazyInitialization(true);
application.setAdditionalProfiles("jdbc");
this.context = application.run();
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()));
this.bookMapper = (rs, rowNum) -> new Book(rs.getLong("id"), rs.getString("title"), rs.getInt("pages"));
}
@SpringBootApplication
static class JdbcApplication {
}
public void close() {
context.close();
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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 java.util.Optional;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.microbenchmark.jdbc.Book;
import org.springframework.data.repository.CrudRepository;
/**
* @author Oliver Drotbohm
*/
public interface JdbcBookRepository extends CrudRepository<Book, Long> {
@Query("SELECT id, title, pages FROM Book where title = :title")
Book findByTitle(String title);
@Query("SELECT id, title, pages FROM Book where title = :title")
Optional<Book> findOptionalByTitle(String title);
}

View File

@@ -0,0 +1,56 @@
/*
* 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

@@ -0,0 +1,38 @@
/*
* 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;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Oliver Drotbohm
*/
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Book {
private @GeneratedValue @Id Long id;
private String title;
private int pages;
}

View File

@@ -0,0 +1,93 @@
/*
* 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;
import java.util.Optional;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
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;
/**
* @author Oliver Drotbohm
*/
public class JpaBenchmark extends AbstractMicrobenchmark {
private JpaFixture fixture;
@Setup
public void setUp() {
this.fixture = new JpaFixture();
}
@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");
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);
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");
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");
sink.consume(Optional.of(query.getSingleResult()));
});
}
@Benchmark
public void findAll(Blackhole sink) {
fixture.withNonTransactionalEntityManager(em -> {
sink.consume(em.createQuery("select b from Book b").getResultList());
});
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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;
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.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
/**
* @author Oliver Drotbohm
*/
public 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();
withTransactionalEntityManager(em -> {
IntStream.range(0, Constants.NUMBER_OF_BOOKS) //
.mapToObj(it -> new Book(null, "title" + it, it)) //
.forEach(em::persist);
});
}
public void withTransactionalEntityManager(Consumer<EntityManager> consumer) {
PlatformTransactionManager manager = context.getBean(PlatformTransactionManager.class);
TransactionStatus status = manager.getTransaction(new DefaultTransactionDefinition());
EntityManager em = context.getBean(EntityManager.class);
consumer.accept(em);
em.flush();
manager.commit(status);
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

@@ -0,0 +1,35 @@
/*
* 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 java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.microbenchmark.jpa.Book;
import org.springframework.data.repository.CrudRepository;
/**
* @author Oliver Drotbohm
*/
public interface JpaBookRepository extends CrudRepository<Book, Long> {
@Query("select b from Book b where b.title = :title")
Book findDeclaredByTitle(String title);
Book findDerivedByTitle(String title);
Optional<Book> findOptionalDerivedByTitle(String title);
}

View File

@@ -0,0 +1,70 @@
/*
* 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 @@
spring.data.jpa.repositories.enabled=false

View File

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

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<groupId>org.springframework.data.benchmark</groupId>
<artifactId>spring-data-benchmark-parent</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>