Generating Letters at Scale With Spring Batch and PDF Templates

October 21, 2026 - Reading time: 3 minutes

Spring Batch suits letter generation because a large run breaks down naturally into reading a record, merging it into a template and writing a PDF. The framework already handles chunking, restarts and job history, so the decisions that shape the result are the template design and the format of the output files, and settling those early keeps reruns predictable across tens of thousands of records.

Choose the Output Format for Each Letter

Decide early whether each recipient gets a separate PDF or a page inside one large file, since that choice affects every later step. One PDF per account keeps each letter tied to a single record, which makes spot checks easier, and it suits collections work, where finished letters can be uploaded to Send Certified Mail for debt collection agencies for printing and mailing. Name each file with the account ID and letter date so a mailing report can be matched back to its source row. Set the page size and margins at this stage as well, so every file comes out print-ready.

Build the Reader, Processor and Writer

The reader pulls account records, the processor turns each one into a letter model holding the merge fields, and the writer renders and saves the PDF. Spring Batch reads and processes items one at a time and then writes the whole chunk and commits once the commit interval is reached, so a chunk size in the low hundreds is a reasonable starting point when every item produces a file. A paging database reader reads one page at a time and saves its position for restarts. Because a retried chunk writes the same files again, deterministic file names let the second attempt overwrite the first instead of leaving duplicates.

Set Up the PDF Template

Rendering an HTML template with merge fields through a library such as OpenHTMLtoPDF is the easier option to maintain because non-developers can edit it, while filling a PDF form with Apache PDFBox suits letters that must match a fixed layout exactly. Load or compile the template once before the first record is processed, in a setup method or a singleton bean, since parsing it for every letter adds up over a large run. Keep the template in source control and record its version in the job parameters, so any finished letter can be traced to the template that produced it.

Scale the Run and Handle Bad Records

Partitioning is the cleaner choice once the run has to finish inside a mailing window, since Spring Batch can divide one step into worker steps that each read their own ID range. The job repository tracks every worker, so a restart repeats only the steps that failed. Configure skip logic with a listener for skipped records so one bad address is written to an error file instead of stopping the run, and reprocess that file afterward.

Before scaling up, run the job against a few hundred sample records and open the resulting PDFs, checking that merge fields, page breaks and file names all look right. Print one or two as well, since template problems are easier to fix early.

by a T.Smaltsar, Editor. 



Category:

Ads are shown when you're not logged in.