{"id":21744153,"url":"https://github.com/hogwai/spring-data-jpa-batch-dml","last_synced_at":"2026-05-10T16:39:54.848Z","repository":{"id":226843636,"uuid":"769779029","full_name":"Hogwai/spring-data-jpa-batch-dml","owner":"Hogwai","description":"Different ways to batch SQL statements with Spring Boot","archived":false,"fork":false,"pushed_at":"2026-02-20T22:26:07.000Z","size":148,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-02-21T04:10:40.781Z","etag":null,"topics":["batch-insert","batch-update","hibernate","java17","jdbc-template","jpa","spring-boot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hogwai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-10T03:06:24.000Z","updated_at":"2026-02-20T22:27:17.000Z","dependencies_parsed_at":"2024-05-13T15:48:29.806Z","dependency_job_id":"cefb9598-d314-4f5f-8c59-f0cf8bea97df","html_url":"https://github.com/Hogwai/spring-data-jpa-batch-dml","commit_stats":null,"previous_names":["hogwai/spring-data-jpa-batch-dml"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Hogwai/spring-data-jpa-batch-dml","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hogwai%2Fspring-data-jpa-batch-dml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hogwai%2Fspring-data-jpa-batch-dml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hogwai%2Fspring-data-jpa-batch-dml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hogwai%2Fspring-data-jpa-batch-dml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hogwai","download_url":"https://codeload.github.com/Hogwai/spring-data-jpa-batch-dml/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hogwai%2Fspring-data-jpa-batch-dml/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32864083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"ssl_error","status_checked_at":"2026-05-10T13:40:02.145Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["batch-insert","batch-update","hibernate","java17","jdbc-template","jpa","spring-boot"],"created_at":"2024-11-26T07:10:12.499Z","updated_at":"2026-05-10T16:39:54.839Z","avatar_url":"https://github.com/Hogwai.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spring Data JPA Batch DML\n\nComparison of 5 batch insert strategies using Spring Data JPA and PostgreSQL.\n\n## Strategies\n\n| Strategy | Description |\n|----------|-------------|\n| **Hibernate Batch** | `EntityManager.persist()` + flush with `hibernate.jdbc.batch_size` and `reWriteBatchedInserts` |\n| **JDBC Batch** | `JdbcTemplate.batchUpdate()` with native SQL |\n| **UNNEST** | `INSERT ... SELECT FROM UNNEST(...)` — single SQL statement using PostgreSQL arrays |\n| **COPY** | `COPY ... FROM STDIN` — PostgreSQL binary protocol via `CopyManager` |\n| **Multi-row VALUES** | `INSERT INTO ... VALUES (...), (...), ...` — single statement with all rows as parameter placeholders |\n\n## Project Structure\n\n```\nspring-data-jpa-batch-dml/\n├── batch-core/          # Models, repositories, services, utilities (JAR)\n├── web/                 # Spring Boot app, controllers, records (bootJar)\n├── benchmark/           # JMH benchmarks\n└── docker-compose.yml   # PostgreSQL 17\n```\n\n### Modules\n\n- **batch-core** — shared business logic (JPA entities, repositories, 5 `CustomerService` implementations)\n- **web** — Spring Boot application with REST API (`/customers`, `/stores`), Spring Boot Admin, Springdoc\n- **benchmark** — JMH benchmarks comparing the 5 batch insert strategies\n\n## Prerequisites\n\n- Java 17+\n- Docker (for PostgreSQL)\n\n## Getting Started\n\n```bash\n# Start PostgreSQL\ndocker compose up -d\n\n# Build\n./gradlew clean build -x test\n\n# Run the application\n./gradlew :web:bootRun\n```\n\nThe application starts on port `8081`.\n\n## API\n\n```bash\n# Batch insert (modes: jdbc, hibernate, unnest, copy, multirow)\ncurl -X POST \"http://localhost:8081/customers/save-all?number=1000\u0026mode=multirow\"\n\n# Get all customers\ncurl http://localhost:8081/customers/get-all\n\n# Get all with store and orders (eager fetch)\ncurl http://localhost:8081/customers/get-all-with-store-orders\n\n# Delete all\ncurl -X DELETE http://localhost:8081/customers/delete-all\n```\n\n## Tests\n\n```bash\n# Unit tests (H2)\n./gradlew :web:test\n\n# Integration tests (requires PostgreSQL)\n./gradlew :batch-core:integrationTest\n```\n\n## JMH Benchmarks\n\n```bash\n# Run benchmarks (requires PostgreSQL)\n./gradlew :benchmark:jmh\n```\n\nResults are written to `benchmark/build/reports/jmh/results.json`.\n\n### Results (1,000 customers + 1,000 orders per batch)\n\n\u003e Environment: JDK 17.0.18 (OpenJDK), PostgreSQL 17 (Docker), `batch_size=100`, `reWriteBatchedInserts=true`\n\u003e\n\u003e JMH config: fork=1, warmup=1x3s, measurement=3x5s, mode=AverageTime\n\n| Strategy | Avg (ms/op) | Error (ms) |\n|----------|-------------|------------|\n| UNNEST | **17.47** | ± 21.90 |\n| Multi-row VALUES | 23.48 | ± 59.64 |\n| JDBC Batch | 23.68 | ± 82.60 |\n| COPY | 42.39 | ± 200.45 |\n| Hibernate Batch | 182.82 | ± 434.61 |\n\n**UNNEST** is the fastest for 1,000 rows, followed closely by **multi-row VALUES** and **JDBC Batch**. **Hibernate Batch** is significantly slower due to the persistence context overhead (dirty checking, cascade).\n\n## Tech Stack\n\n- Spring Boot 3.2.2\n- Spring Data JPA / Hibernate 6.4\n- PostgreSQL 17\n- Lombok\n- JMH 1.37\n- Gradle 8.5 (multi-module)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhogwai%2Fspring-data-jpa-batch-dml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhogwai%2Fspring-data-jpa-batch-dml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhogwai%2Fspring-data-jpa-batch-dml/lists"}