Introduction Multi-threaded steps (Article 20) run multiple chunks concurrently from a single reader. Partitioning is different — it splits the data into independent slices before processing starts, then runs each slice as its own StepExecution with its own reader, processor, writer, and metadata. This gives you: True independence between partitions — one partition’s failure doesn’t affect others A separate StepExecution row per partition in BATCH_STEP_EXECUTION — full visibility into per-partition progress The ability to distribute partitions across multiple JVMs (remote partitioning, covered in Article 22) Partitioning Architecture ManagerStep (PartitionStep) │ ├── Partitioner → creates N ExecutionContexts (one per partition) ├── PartitionHandler → distributes partitions to workers │ └── Worker Steps (run per partition) ├── ItemReader → reads only its slice of data ├── ItemProcessor └── ItemWriter The manager step runs once: it calls Partitioner.
Continue reading »Java
223 posts in this section
Password Encoding: BCrypt, Argon2, and DelegatingPasswordEncoder
Why Passwords Must Be Hashed Storing plaintext passwords is a critical security failure. When a database is breached, attackers immediately have every user’s password — and because people reuse passwords, those credentials work on other sites too. Password hashing is not encryption. Encryption is reversible. Hashing is one-way: you can verify a password by hashing it and comparing to the stored hash, but you cannot recover the original password from the hash.
Continue reading »Password Management: Registration, Reset, and Migration
Secure Registration Registration is where passwords enter your system. Get it wrong here and no amount of downstream security saves you. The Registration Flow User submits form → Validate password strength → Check username/email uniqueness → Encode password with PasswordEncoder → Persist user (disabled) → Send verification email → User clicks link → enable account Registration Endpoint @RestController @RequestMapping("/auth") public class RegistrationController { private final UserService userService; private final PasswordEncoder passwordEncoder; @PostMapping("/register") public ResponseEntity<Void> register(@Valid @RequestBody RegistrationRequest request) { userService.
Continue reading »Pattern Matching for instanceof (JEP 394): Smarter Type Checks
Finalized in Java 16 (JEP 394). Available in all Java 16+ releases, including Java 17. Previous previews: Java 14 (JEP 305) and Java 15 (JEP 375). The Problem: The instanceof-Cast Dance Every Java developer has written this pattern: // Java 11 — check, then cast if (obj instanceof String) { String s = (String) obj; // cast is logically redundant System.out.println(s.toUpperCase()); } The instanceof check already verified the type. The cast on the next line is conceptually redundant — the compiler could infer that obj is a String in the if body.
Continue reading »Pattern Matching for switch (JEP 406): Type Dispatch in switch (Preview)
Preview Feature in Java 17 (JEP 406 — First Preview). Requires --enable-preview. This feature evolved through Java 18 (JEP 420), Java 19 (JEP 427), Java 20 (JEP 433), and was finalized in Java 21 (JEP 441). The Java 21 final version has minor syntax differences from this Java 17 preview — documented below. Enabling Preview Features Pattern matching for switch requires --enable-preview in Java 17. Add to your build tool:
Continue reading »Pattern Matching for switch (JEP 441): Type Dispatch Without the Boilerplate
The Problem: Cascading instanceof Chains Any Java codebase handling multiple subtypes has code like this: // Java 16 and earlier — the bad old way static double calculateArea(Shape shape) { if (shape instanceof Circle) { Circle c = (Circle) shape; return Math.PI * c.radius() * c.radius(); } else if (shape instanceof Rectangle) { Rectangle r = (Rectangle) shape; return r.width() * r.height(); } else if (shape instanceof Triangle) { Triangle t = (Triangle) shape; return 0.
Continue reading »Pausing, Resuming, and Stopping Listener Containers
Why Control Container Lifecycle? A running listener consumes from Kafka continuously. In production you need to: Pause consumption when a downstream service is overloaded (back-pressure) Resume once the downstream recovers Stop a container entirely during maintenance or feature flag toggles Restart after a configuration change without redeploying Spring Kafka exposes all of this through KafkaListenerEndpointRegistry and the container’s own lifecycle API. Container States stateDiagram-v2 [*] --> Running : start() Running --> Paused : pause() Paused --> Running : resume() Running --> Stopped : stop() Stopped --> Running : start() Paused --> Stopped : stop() Running — polling Kafka, dispatching to listener Paused — broker connection maintained, consumer heartbeat sent, no new records fetched Stopped — consumer thread terminated, partitions released back to group Paused is preferable to stopped for temporary throttling: it avoids a rebalance and keeps the consumer’s partition assignment intact.
Continue reading »Performance Tuning: Chunk Size, Connection Pools, and Memory Management
Introduction A poorly tuned batch job can be 10–100x slower than a well-tuned one. The biggest gains come from a handful of settings — chunk size, MySQL JDBC rewrite, connection pool alignment, and avoiding unnecessary object creation. This article covers each systematically. Chunk Size — The Most Impactful Setting Chunk size determines how many items are processed per transaction. Too small = too many round trips to the database. Too large = long transactions, high memory pressure, slower rollback on failure.
Continue reading »Primary Keys and Generated Values: IDENTITY, SEQUENCE, UUID
Introduction Primary key choice affects performance, scalability, and application design. This article covers every strategy JPA supports — from the simple AUTO_INCREMENT to UUID and composite keys — with the trade-offs of each. IDENTITY Strategy (MySQL AUTO_INCREMENT) GenerationType.IDENTITY delegates key generation to the database column’s auto-increment feature. This is the standard for MySQL. @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; Generated DDL (when ddl-auto=create): id BIGINT NOT NULL AUTO_INCREMENT How IDENTITY works with Hibernate IDENTITY has one important behaviour: Hibernate cannot batch INSERT statements when using IDENTITY.
Continue reading »Producer @Bean Configuration: Beyond application.properties
Why @Bean Configuration? application.properties is convenient for a single producer, but insufficient when you need: Multiple producers with different serializers (e.g. one for JSON events, one for Avro) Different settings per environment built at runtime (not just property substitution) Producers sending to different clusters (e.g. primary + DR cluster) Programmatic validation of configuration at startup flowchart TB subgraph PropertiesApproach["application.properties Approach"] P1["Single producer config\nspring.kafka.producer.*\n✓ Simple\n✗ One producer only\n✗ No runtime logic"] end subgraph BeanApproach["
Continue reading »