BAEL-5545: prepared a simple demo to benchmark spring jdbc batch inserts

This commit is contained in:
Elmar Mammadov 2022-07-08 01:35:31 +02:00
parent b045e8309d
commit eafb271394
10 changed files with 333 additions and 0 deletions

View File

@ -105,6 +105,7 @@
<module>spring-jooq</module>
<module>spring-mybatis</module>
<module>spring-persistence-simple</module>
<module>spring-jdbc-batch</module>
</modules>
<properties>

View File

@ -0,0 +1,60 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-jdbc-batch</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-jdbc-batch</name>
<description>Demo project for Spring Boot Jdbc batch support</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
</parent>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,43 @@
package com.baeldung.spring.jdbc.batch;
import com.baeldung.spring.jdbc.batch.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringJdbcBatchPerformanceApplication implements CommandLineRunner {
@Autowired
@Qualifier("batchProductService")
private ProductService batchProductService;
@Autowired
@Qualifier("simpleProductService")
private ProductService simpleProductService;
public static void main(String[] args) {
SpringApplication.run(SpringJdbcBatchPerformanceApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
int[] recordCounts = { 1, 10, 100, 1000, 10_000, 100_000, 1000_000 };
for (int recordCount : recordCounts) {
long regularElapsedTime = simpleProductService.createProducts(recordCount);
long batchElapsedTime = batchProductService.createProducts(recordCount);
System.out.println("-".repeat(50));
System.out.format("%-20s%-5s%-10s%-5s%8sms\n", "Regular inserts", "|", recordCount, "|", regularElapsedTime);
System.out.format("%-20s%-5s%-10s%-5s%8sms\n", "Batch inserts", "|", recordCount, "|", batchElapsedTime);
System.out.printf("Total gain: %d %s\n", calculateGainInPercent(regularElapsedTime, batchElapsedTime), "%");
}
}
int calculateGainInPercent(long before, long after) {
return (int) Math.floor(100D * (before - after) / before);
}
}

View File

@ -0,0 +1,24 @@
package com.baeldung.spring.jdbc.batch.config;
import com.baeldung.spring.jdbc.batch.repo.BatchProductRepository;
import com.baeldung.spring.jdbc.batch.repo.SimpleProductRepository;
import com.baeldung.spring.jdbc.batch.service.ProductService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Clock;
import java.util.Random;
@Configuration
public class AppConfig {
@Bean
public ProductService simpleProductService(SimpleProductRepository simpleProductRepository) {
return new ProductService(simpleProductRepository, new Random(), Clock.systemUTC());
}
@Bean
public ProductService batchProductService(BatchProductRepository batchProductRepository) {
return new ProductService(batchProductRepository, new Random(), Clock.systemUTC());
}
}

View File

@ -0,0 +1,54 @@
package com.baeldung.spring.jdbc.batch.model;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class Product {
private long id;
private String title;
private LocalDateTime createdTs;
private BigDecimal price;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public LocalDateTime getCreatedTs() {
return createdTs;
}
public void setCreatedTs(LocalDateTime createdTs) {
this.createdTs = createdTs;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Product{");
sb.append("id=").append(id);
sb.append(", title='").append(title).append('\'');
sb.append(", createdTs=").append(createdTs);
sb.append(", price=").append(price);
sb.append('}');
return sb.toString();
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.spring.jdbc.batch.repo;
import com.baeldung.spring.jdbc.batch.model.Product;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.List;
@Repository
public class BatchProductRepository implements ProductRepository {
private final JdbcTemplate jdbcTemplate;
public BatchProductRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
@Transactional
public void saveAll(List<Product> products) {
jdbcTemplate.batchUpdate("INSERT INTO PRODUCT (TITLE, CREATED_TS, PRICE) VALUES (?, ?, ?)",
products,
100,
(PreparedStatement ps, Product product) -> {
ps.setString(1, product.getTitle());
ps.setTimestamp(2, Timestamp.valueOf(product.getCreatedTs()));
ps.setBigDecimal(3, product.getPrice());
});
}
}

View File

@ -0,0 +1,9 @@
package com.baeldung.spring.jdbc.batch.repo;
import com.baeldung.spring.jdbc.batch.model.Product;
import java.util.List;
public interface ProductRepository {
void saveAll(List<Product> products);
}

View File

@ -0,0 +1,29 @@
package com.baeldung.spring.jdbc.batch.repo;
import com.baeldung.spring.jdbc.batch.model.Product;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.sql.Timestamp;
import java.util.List;
@Repository
public class SimpleProductRepository implements ProductRepository {
private final JdbcTemplate jdbcTemplate;
public SimpleProductRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
@Transactional
public void saveAll(List<Product> products) {
for (Product product : products) {
jdbcTemplate.update("INSERT INTO PRODUCT (TITLE, CREATED_TS, PRICE) VALUES (?, ?, ?)",
product.getTitle(), Timestamp.valueOf(product.getCreatedTs()), product.getPrice());
}
}
}

View File

@ -0,0 +1,54 @@
package com.baeldung.spring.jdbc.batch.service;
import com.baeldung.spring.jdbc.batch.model.Product;
import com.baeldung.spring.jdbc.batch.repo.ProductRepository;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ProductService {
private final ProductRepository productRepository;
private final Random random;
private final Clock clock;
public ProductService(ProductRepository productRepository, Random random, Clock clock) {
this.productRepository = productRepository;
this.random = random;
this.clock = clock;
}
@Transactional
public long createProducts(int count) {
List<Product> products = generate(count);
long startTime = clock.millis();
productRepository.saveAll(products);
return clock.millis() - startTime;
}
protected List<Product> generate(int count) {
final String[] titles = { "car", "plane", "house", "yacht" };
final BigDecimal[] prices = {
new BigDecimal("12483.12"),
new BigDecimal("8539.99"),
new BigDecimal("88894"),
new BigDecimal("458694")
};
final List<Product> products = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
Product product = new Product();
product.setCreatedTs(LocalDateTime.now());
product.setPrice(prices[random.nextInt(4)]);
product.setTitle(titles[random.nextInt(4)]);
products.add(product);
}
return products;
}
}

View File

@ -0,0 +1,26 @@
package com.baeldung.spring.jdbc.batch.service;
import com.baeldung.spring.jdbc.batch.repo.ProductRepository;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.time.Clock;
import java.util.Random;
class ProductServiceUnitTest {
@Mock
ProductRepository productRepository;
@Mock
Random random;
@Mock
Clock clock;
@InjectMocks
ProductService productService;
@Test
void testWhenThen() {
}
}