<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey" -->

---
title: DuckDB in Spring Batch: Replace In-Memory Java Loops...
description: A practical benchmark comparing two Spring Batch transform approaches: a traditional in-memory Java HashMap loop versus a single DuckDB SQL statement using its...
canonical: https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: DuckDB in Spring Batch: Replace In-Memory Java Loops with One SQL Statement | daily.dev
og:description: A practical benchmark comparing two Spring Batch transform approaches: a traditional in-memory Java HashMap loop versus a single DuckDB SQL statement using its...
og:url: https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey
og:image: https://api.daily.dev/og/posts/Fozy2ZFEY.png
og:image:alt: DuckDB in Spring Batch: Replace In-Memory Java Loops with One SQL Statement
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# DuckDB in Spring Batch: Replace In-Memory Java Loops with One SQL Statement

**[Foojay.io](https://daily.dev/sources/foojayio)** · 5 min read · 6 upvotes · 0 comments

## Summary

A practical benchmark comparing two Spring Batch transform approaches: a traditional in-memory Java HashMap loop versus a single DuckDB SQL statement using its embedded JDBC driver. On 10 million rows, DuckDB is ~6× faster; on 50 million rows, ~8× faster. The speedup comes from DuckDB's columnar vectorization (batches of ~2,048 values), automatic multi-core parallelism, and elimination of per-row object allocation and GC pressure. The post includes full runnable code for both approaches and recommends DuckDB specifically for aggregation-heavy transform steps over files or databases, while keeping the classic reader/processor/writer pattern for row-level enrichment or external API calls.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://foojay.io/today/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement>

## Questions this post answers

### How much faster is DuckDB than a hand-written Java HashMap loop for aggregating CSV data in a Spring Batch transform step?

DuckDB is roughly 6 to 8 times faster. On 10 million rows, a vectorized DuckDB SQL query took 0.17 seconds versus 1.00 second for an in-memory Java HashMap loop; on 50 million rows, DuckDB took 0.61 seconds versus 4.83 seconds for Java, measured on a 12-thread Apple Silicon machine with Java 21 and DuckDB 1.5.5.

_Weighing whether to offload batch aggregation to embedded SQL is easier with benchmarks like this on daily.dev._

### How do I run a DuckDB SQL query directly inside a Spring Batch Tasklet instead of using ItemReader and ItemProcessor?

Open a plain JDBC connection with DriverManager.getConnection("jdbc:duckdb:"), then execute a single SQL statement inside the Tasklet, such as a COPY command that reads a CSV with read_csv(), groups and aggregates it, and writes results to another CSV. This replaces the reader, processor, and writer entirely for transform-heavy steps, requiring only the org.duckdb:duckdb_jdbc dependency and no extra infrastructure.

_Developers restructuring batch pipelines can keep up with patterns like this DuckDB Tasklet approach on daily.dev._

### When should I keep the classic Spring Batch reader-processor-writer pattern instead of using DuckDB for a transform step?

Keep the classic chunk-oriented reader, processor, and writer pattern for row-level enrichment, calls to external services, or writes to a strict downstream system, since Spring Batch's chunk model suits orchestration, restartability, and I/O well. Reserve DuckDB for transform steps that aggregate, join, or derive values over large volumes from a file or a source DuckDB can read.

_Choosing between chunk-based processing and SQL-based aggregation gets easier by comparing real trade-offs on daily.dev._

## Similar posts on daily.dev

- [Why DuckDB is my first choice for data processing](https://daily.dev/posts/why-duckdb-is-my-first-choice-for-data-processing-sx7gglwxx) · Hacker News · 1 upvotes · 0 comments
- [Importing Data using Java Table Functions](https://daily.dev/posts/importing-data-using-java-table-functions-sorg61u98) · DuckDB · 0 upvotes · 1 comments
- [Embedding DuckDB in a Maven App \(and Using It for Things That Aren’t Databases\)](https://daily.dev/posts/embedding-duckdb-in-a-maven-app-and-using-it-for-things-that-aren-t-databases--ynaml3z15) · Foojay.io · 0 upvotes · 0 comments
- [The DuckDB MySQL engine at 500 GB](https://daily.dev/posts/the-duckdb-mysql-engine-at-500-gb-baqjll9pk) · Planet MySQL · 0 upvotes · 0 comments
- [DuckDB 1.5 is Faster and Easier than Ever](https://daily.dev/posts/duckdb-1-5-is-faster-and-easier-than-ever-9a69bd0zb) · MotherDuck · 68 upvotes · 0 comments

---

Tags: [#java](https://daily.dev/tags/java), [#spring](https://daily.dev/tags/spring), [#duckdb](https://daily.dev/tags/duckdb)

[View this post on daily.dev](https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"DuckDB in Spring Batch: Replace In-Memory Java Loops with One SQL Statement","url":"https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey"},"datePublished":"2026-08-11T13:40:17.996Z","dateModified":"2026-09-13T21:22:16.766Z","description":"A practical benchmark comparing two Spring Batch transform approaches: a traditional in-memory Java HashMap loop versus a single DuckDB SQL statement using its...","image":"https://media.daily.dev/image/upload/s--ZrL_HSsR--/f_auto/v1722860399/public/Placeholder%2006","thumbnailUrl":"https://media.daily.dev/image/upload/s--ZrL_HSsR--/f_auto/v1722860399/public/Placeholder%2006","isAccessibleForFree":true,"articleSection":"Foojay.io","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Foojay.io","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/1820f6a6ae944760aa322b67ed85f848","url":"https://daily.dev/sources/foojayio"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":6},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"java,spring,duckdb","timeRequired":"PT5M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Foojay.io","item":"https://daily.dev/sources/foojayio"},{"@type":"ListItem","position":3,"name":"DuckDB in Spring Batch: Replace In-Memory Java Loops with One SQL Statement"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement-fozy2zfey#faq","mainEntity":[{"@type":"Question","name":"How much faster is DuckDB than a hand-written Java HashMap loop for aggregating CSV data in a Spring Batch transform step?","acceptedAnswer":{"@type":"Answer","text":"DuckDB is roughly 6 to 8 times faster. On 10 million rows, a vectorized DuckDB SQL query took 0.17 seconds versus 1.00 second for an in-memory Java HashMap loop; on 50 million rows, DuckDB took 0.61 seconds versus 4.83 seconds for Java, measured on a 12-thread Apple Silicon machine with Java 21 and DuckDB 1.5.5. Weighing whether to offload batch aggregation to embedded SQL is easier with benchmarks like this on daily.dev."}},{"@type":"Question","name":"How do I run a DuckDB SQL query directly inside a Spring Batch Tasklet instead of using ItemReader and ItemProcessor?","acceptedAnswer":{"@type":"Answer","text":"Open a plain JDBC connection with DriverManager.getConnection(\"jdbc:duckdb:\"), then execute a single SQL statement inside the Tasklet, such as a COPY command that reads a CSV with read_csv(), groups and aggregates it, and writes results to another CSV. This replaces the reader, processor, and writer entirely for transform-heavy steps, requiring only the org.duckdb:duckdb_jdbc dependency and no extra infrastructure. Developers restructuring batch pipelines can keep up with patterns like this DuckDB Tasklet approach on daily.dev."}},{"@type":"Question","name":"When should I keep the classic Spring Batch reader-processor-writer pattern instead of using DuckDB for a transform step?","acceptedAnswer":{"@type":"Answer","text":"Keep the classic chunk-oriented reader, processor, and writer pattern for row-level enrichment, calls to external services, or writes to a strict downstream system, since Spring Batch's chunk model suits orchestration, restartability, and I/O well. Reserve DuckDB for transform steps that aggregate, join, or derive values over large volumes from a file or a source DuckDB can read. Choosing between chunk-based processing and SQL-based aggregation gets easier by comparing real trade-offs on daily.dev."}}]}
```

