iComposer Batch is a batch job orchestration platform built on Spring Batch. It supports dynamically defining, configuring, and executing batch jobs through Groovy DSL. This document covers the core concepts, operational workflows, and code templates.
Table of Contents
- Core Concepts
- Data Model
- Operational Workflow
- Step Types
- Item Types & Code Templates
- Listener Types
- Common Quick Methods
- Best Practices
Core Concepts
| Concept | Description |
|---|---|
| Batch Job | A batch processing task containing metadata (name, module, group, status) and an ordered set of Steps |
| Step | A processing unit within a Job, supporting Chunk (batch processing) and Tasklet (single task) modes |
| Step Item | A component inside a Step, including Reader, Processor, Writer, Listener, etc. |
| Chunk | Spring Batch’s chunk-oriented processing pattern: Reader reads a batch → Processor processes items one by one → Writer writes the batch |
Execution Flow:
Batch Job
└─ Step 1 (Chunk mode)
│ ├─ ItemReader / ItemStreamReader / ItemEnhancedPagingReader
│ ├─ ItemProcessor
│ ├─ ItemWriter
│ └─ Listeners (Step / Read / Process / Write / Skip)
└─ Step 2 (Tasklet mode)
├─ Tasklet
└─ ListenersData Model
Batch Job
| Field | Description | Constraints |
|---|---|---|
| BatchName | Batch name | Must match [A-Z][0-9A-Za-z_]* |
| JobName | Spring Batch Job name | Runtime identifier |
| BatchType | Batch type | — |
| Status | Status | 1 = Active |
| ModuleId | Owning module | Links to a business module |
| GroupId | Group | Used for grouped management and import/export |
| Description | Description | — |
Batch Step
| Field | Description | Constraints |
|---|---|---|
| StepName | Step name | Must follow naming conventions |
| StepType | Step type | 1 = Chunk, 2 = Tasklet |
| Seq | Execution order | Numeric value determining step execution order |
| ChunkSize | Chunk size | Number of records per chunk in Chunk mode |
| ThrottleLimit | Concurrency limit | Number of concurrent processing threads |
| SkipLimit | Skip limit | Maximum allowed errors before failing |
| Status | Status | — |
Batch Step Item
| Field | Description | Constraints |
|---|---|---|
| ItemName | Component name | Must follow naming conventions |
| ItemType | Component type | Reader / Processor / Writer / Tasklet, etc. |
| ImplementType | Implementation type | Specific implementation variant (includes Listener subtypes) |
| Content | Groovy code | Script content compiled and executed at runtime |
| StaticCompile | Static compilation | Y / N; enabling improves performance |
| FoundationDisabled | Foundation whitelist | Y / N |
Operational Workflow
1. Create a Batch Job
- Navigate to iComposer → Batch page
- Click New, fill in BatchName, JobName, Module, Group, etc.
- Save
2. Configure Steps
- Open the Job detail page and add a Step
- Select StepType (Chunk or Tasklet)
- Configure ChunkSize (for Chunk mode), SkipLimit, and ThrottleLimit
- Set Seq to define the step execution order
3. Write Item Code
- Add an Item (Reader / Processor / Writer / Tasklet) to the Step
- Select the ImplementType — the system auto-loads the corresponding template
- Write business logic at the
//TODOmarkers - Save and compile
4. Execute & Monitor
- Submit the job (call the Submit Job API)
- Query execution status via JobExecutionId
- View Step execution details and error logs
5. Import & Export
- Export Batch configurations in bulk by GroupId
- Import exported configurations into other environments
Step Types
Chunk Mode (StepType = 1)
Processes data in chunks, suitable for large-volume ETL scenarios:
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ ItemReader │ ──→ │ ItemProcessor │ ──→ │ ItemWriter │
│ Read N items │ │ Process each │ │ Write N items│
└─────────────┘ └──────────────┘ └─────────────┘
↑ │
└──────── Repeat until exhausted ─────┘- ChunkSize determines the number of records per read → process → write cycle
- Configure SkipLimit to tolerate error records
- Configure ThrottleLimit to control concurrency
Tasklet Mode (StepType = 2)
Executes a single task, suitable for initialization, cleanup, notification, and other non-data-flow scenarios:
┌─────────────┐
│ Tasklet │ Execute a custom operation once
└─────────────┘Item Types & Code Templates
ItemReader — Data Reading
Reads from a data source and returns a list of objects. Each invocation returns a List; the framework passes items one by one to the Processor.
/*
* Implement Type : ItemReader
* return: object list
* Quick Method:
* 1. Map getParameters()
* 2. StepExecution getStepExecution()
*/
import org.springframework.batch.core.StepExecution;
StepExecution stepExecution = getStepExecution();
def params = getParameters();
// TODO: Query data and return a list of records
def data = ["1", "2"]
return data;Use case: Small data volumes that can be loaded into memory at once.
ItemStreamReader — Streaming Paged Reading
Streams data with paged queries and cursor-based positioning. Each invocation returns a single object.
/*
* Implement Type : ItemStreamReader
* return: single object (return null to signal end of stream)
* Quick Method:
* 1. Map getParameters()
* 2. StepExecution getStepExecution()
* 3. long getPreDetailId() / setPreDetailId(long)
* 4. int getPos() / setPos(int)
* 5. List getObjectList() / setObjectList(List)
*/
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.batch.core.StepExecution;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
def objectList = getObjectList(); // Currently queried records
def preId = getPreDetailId(); // Last ID from the previous query
def pos = getPos(); // Current traversal position
// First query or current batch exhausted — fetch the next batch
if (CollectionUtils.isEmpty(objectList) || pos >= objectList.size() - 1) {
// TODO: Replace with actual paged query logic, using preId as cursor
objectList = ["111", "222", "333"];
setObjectList(objectList); // Save the latest query results
if (CollectionUtils.isEmpty(objectList)) {
return null; // Empty result means all data has been read
}
pos = -1; // Reset position
}
pos++;
Object object = objectList.get(pos);
preId = 0L; // TODO: Replace with object.getXXXId()
setPos(pos);
setPreDetailId(preId);
return object;Use case: Large data volumes — paged queries prevent OOM; uses ID-based cursor pagination.
ItemEnhancedPagingReader — Enhanced Paged Reading
The framework automatically manages pagination parameters (startIdNotIncluded / maxRecords). Each invocation returns a batch of data.
/*
* Implement Type : ItemEnhancedPagingReader
* return: object list (return null to signal end of data)
* Quick Method:
* 1. Map getParameters()
* 2. StepExecution getStepExecution()
* 3. String getIdField() / setIdField(String)
* 4. Long startIdNotIncluded / int maxRecords
*/
import org.apache.commons.lang3.StringUtils;
import org.springframework.batch.core.StepExecution;
info("params: startIdNotIncluded={},maxRecords={}", startIdNotIncluded, maxRecords)
// TODO: Replace termination condition with actual logic
if (startIdNotIncluded >= 2) return null;
def parameters = getParameters();
def idField = getIdField();
if (StringUtils.isEmpty(idField)) {
setIdField("XXXX"); // TODO: Replace with actual primary key field name, e.g. "id"
}
StepExecution stepExecution = getStepExecution();
// TODO: Use startIdNotIncluded and maxRecords for paged query
// SQL example: SELECT * FROM table WHERE id > startIdNotIncluded ORDER BY id LIMIT maxRecords
def dataList = [["id": 1], ["id": 2]]
return dataList;Use case: Large data volumes with auto-increment primary key. The framework auto-increments startIdNotIncluded; maxRecords is derived from ChunkSize.
ItemProcessor — Data Processing
Processes items read by the Reader one by one. Supports transformation, validation, enrichment, etc.
/*
* Implement Type : ItemProcessor
* return: processed object (return null to filter out the item)
* Quick Method:
* 1. Map getParameters()
* 2. StepExecution getStepExecution()
* 3. Object getItemObject()
*/
import org.springframework.batch.core.StepExecution;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
def item = getItemObject();
// TODO: Write processing logic
return item;Note: Returning null filters out the item — it will not be passed to the Writer.
ItemWriter — Data Writing
Receives a batch of processed items from the Processor and performs write operations (DB insert, API call, file output, etc.).
/*
* Implement Type : ItemWriter
* return: none
* Quick Method:
* 1. Map getParameters()
* 2. StepExecution getStepExecution()
* 3. List getItemObjectList()
*/
import org.springframework.batch.core.StepExecution;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
def items = getItemObjectList();
info("Write data list, record count: " + items.size());
// TODO: Write batch insert/update logicNote: The list size returned by getItemObjectList() equals ChunkSize (the last batch may be smaller).
Tasklet — Custom Task
Executes a one-shot operation not involving data flow, such as sending notifications, initializing environments, or cleaning up temporary data.
/*
* Implement Type : Tasklet
* return: none
* Quick Method:
* 1. Map getParameters()
* 2. StepExecution getStepExecution()
*/
import org.springframework.batch.core.StepExecution;
def parameter = getParameters();
StepExecution stepExecution = getStepExecution();
// TODO: Write business logicListener Types
Listeners inject callback logic at various stages of batch execution (logging, monitoring, error handling, etc.). A single Listener Item can contain multiple method implementations, separated by //==========ICOMPOSER SPLIT LINE==========.
StepExecutionListener — Step Listener
Triggers callbacks before and after Step execution.
/*--- beforeStep ---*/
/*
* Method: beforeStep
* return: object
* Quick Method: getParameters(), getStepExecution()
*/
import org.springframework.batch.core.StepExecution;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
info("Before Step: " + stepExecution.getStepName());
//==========ICOMPOSER SPLIT LINE==========
/*--- afterStep ---*/
/*
* Method: afterStep
* return: ExitStatus
* Quick Method: getParameters(), getStepExecution()
*/
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.ExitStatus;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
info("After Step: " + stepExecution.getStepName());
return stepExecution.getExitStatus();JobExecutionListener — Job Listener
Triggers callbacks before and after the entire Job execution.
/*--- beforeJob ---*/
/*
* Method: beforeJob
* return: object
* Quick Method: getParameters(), getJobExecution()
*/
import org.springframework.batch.core.JobExecution;
def parameters = getParameters();
JobExecution jobExecution = getJobExecution();
info("Before Job: " + jobExecution.getJobId());
//==========ICOMPOSER SPLIT LINE==========
/*--- afterJob ---*/
/*
* Method: afterJob
* return: object
* Quick Method: getParameters(), getJobExecution()
*/
import org.springframework.batch.core.JobExecution;
def parameters = getParameters();
JobExecution jobExecution = getJobExecution();
info("After Job: " + jobExecution.getJobId());ReadListener — Read Listener
Triggers before/after ItemReader reads data, and on read errors.
| Method | Trigger | Available Quick Methods |
|---|---|---|
beforeRead | Before each read | getParameters(), getStepExecution() |
afterRead | After each read | getParameters(), getStepExecution(), getItemObject() |
onReadError | On read exception | getParameters(), getStepExecution(), getReadException() |
ProcessListener — Process Listener
Triggers before/after ItemProcessor processes data, and on processing errors.
| Method | Trigger | Available Quick Methods |
|---|---|---|
beforeProcess | Before processing | getParameters(), getStepExecution(), getItemObject() |
afterProcess | After processing | getParameters(), getStepExecution(), getItemObject(), getProcessResult() |
onProcessError | On process exception | getParameters(), getStepExecution(), getItemObject(), getProcessException() |
WriteListener — Write Listener
Triggers before/after ItemWriter writes data, and on write errors.
| Method | Trigger | Available Quick Methods |
|---|---|---|
beforeWrite | Before writing | getParameters(), getStepExecution(), getItemObjectList() |
afterWrite | After writing | getParameters(), getStepExecution(), getItemObjectList() |
onWriteError | On write exception | getParameters(), getStepExecution(), getItemObjectList(), getWriteException() |
SkipListener — Skip Listener
Triggers when a data item is skipped due to an exception (requires SkipLimit > 0).
/*--- skipInRead ---*/
/*
* Method: skipInRead
* Quick Method: getParameters(), getStepExecution(), getSkipInReadThrowable()
*/
import org.springframework.batch.core.StepExecution;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
info("Skip In Read: " + stepExecution.getStepName());
//==========ICOMPOSER SPLIT LINE==========
/*--- skipInProcess ---*/
/*
* Method: skipInProcess
* Quick Method: getParameters(), getStepExecution(),
* getSkipInProcessItemObject(), getSkipInProcessThrowable()
*/
import org.springframework.batch.core.StepExecution;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
info("Skip In Process: " + stepExecution.getStepName());
//==========ICOMPOSER SPLIT LINE==========
/*--- skipInWrite ---*/
/*
* Method: skipInWrite
* Quick Method: getParameters(), getStepExecution(),
* getSkipInWriteItemObject(), getSkipInWriteThrowable()
*/
import org.springframework.batch.core.StepExecution;
def parameters = getParameters();
StepExecution stepExecution = getStepExecution();
info("Skip In Write: " + stepExecution.getStepName());Common Quick Methods
All Item types can use the following built-in methods:
| Method | Return Type | Description |
|---|---|---|
getParameters() | Map | Returns the parameters passed at job startup |
getStepExecution() | StepExecution | Returns the current Step’s execution context |
info(String msg) | void | Logs at INFO level |
info(String fmt, Object... args) | void | Logs at INFO level with format placeholders |
Item-specific Quick Methods:
| Item Type | Quick Method | Description |
|---|---|---|
| ItemProcessor | getItemObject() | Returns the current item to be processed |
| ItemWriter | getItemObjectList() | Returns the current list of items to write |
| ItemStreamReader | getPos() / setPos(int) | Get/set current traversal position |
getPreDetailId() / setPreDetailId(long) | Get/set last ID from previous query | |
getObjectList() / setObjectList(List) | Get/set current batch of data | |
| ItemEnhancedPagingReader | getIdField() / setIdField(String) | Get/set primary key field name |
startIdNotIncluded | Starting ID (exclusive), auto-incremented by framework | |
maxRecords | Max records per batch, derived from ChunkSize | |
| Job Listener | getJobExecution() | Returns the Job execution context |
Best Practices
Choosing a Reader
| Scenario | Recommended Reader | Reason |
|---|---|---|
| Small data volume (< 1000 records) | ItemReader | Simple and direct — one-time load |
| Large data volume, cursor pagination needed | ItemStreamReader | Manual control over paging logic and cursor |
| Large data volume, auto-increment PK available | ItemEnhancedPagingReader | Framework manages paging automatically — minimal code |
ChunkSize Configuration
- General scenarios: 50 ~ 200
- External API calls involved: 10 ~ 50 (reduces timeout risk)
- Pure database read/write: 200 ~ 1000
Error Handling
- Set a reasonable SkipLimit to allow minor data errors without aborting the job
- Use SkipListener to log skipped records for post-mortem analysis
- Use
try-catchin the Processor for predictable exceptions to prevent overall failure
Logging Guidelines
- Use the
info()method — do not useprintln - Include key business identifiers in log messages (e.g., record ID, batch number)
- Listener logs should include Step/Job names for traceability
Naming Conventions
- BatchName: UPPER_SNAKE_CASE, e.g.,
POLICY_MIGRATION_JOB - StepName: Descriptive, e.g.,
READ_POLICY_DATA,PROCESS_CLAIM - ItemName: Reflect type and responsibility, e.g.,
PolicyItemReader,ClaimItemProcessor
Performance Optimization
- Avoid full table scans in Readers — use WHERE conditions and pagination
- Use batch operations in Writers (e.g., batch insert) — do not write records one by one
- Enable
StaticCompile = Yto improve Groovy execution performance - Configure
ThrottleLimitappropriately for parallel processing
Multi-Step Coordination
- Use
StepExecution.getExecutionContext()to pass data between steps - Strictly control step execution order via the
Seqfield - Tasklet steps are ideal for pre-validation or post-notification