<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Mysql on Devops Monk</title><link>https://blog.devops-monk.com/tags/mysql/</link><description>Recent content in Mysql on Devops Monk</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 23 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.devops-monk.com/tags/mysql/index.xml" rel="self" type="application/rss+xml"/><item><title>MySQL Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/mysql-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/mysql-testing/</guid><description>MySQL is still the most widely deployed relational database in Java applications. Its SQL dialect, strict mode behavior, and character set handling differ from both H2 and PostgreSQL in ways that matter for real applications. This article covers MySQL testing with Testcontainers — from basic setup to MySQL-specific features and the STRICT_TRANS_TABLES mode that catches data truncation bugs H2 silently ignores.
What You&amp;rsquo;ll Learn MySQLContainer setup and configuration @ServiceConnection with MySQL MySQL strict mode and why it matters for tests Character set and collation configuration Testing MySQL-specific SQL functions MariaDB as an alternative Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>Building a Full-Stack Personalised Gifts E-Commerce Platform with Next.js 15</title><link>https://blog.devops-monk.com/2026/05/personalised-gifts-ecommerce-platform/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/personalised-gifts-ecommerce-platform/</guid><description>Building an e-commerce site is one of those projects that sounds simple until you actually start. Payments, authentication, inventory, personalisation, image handling, email notifications, admin tools, live chat — each piece is its own rabbit hole. I built PersonalisedGifts, a full UK-market personalised gifts store from scratch, wiring all of these together into a single cohesive product.
The site lets customers browse gifts by category (Mugs, Jewellery, Canvas Prints, Home Décor) or occasion (Birthday, Wedding, Christmas, New Baby), personalise their chosen item with custom text, images, fonts, or colours, and pay via Stripe (cards, Apple Pay, Google Pay, Klarna) or directly from their bank account via Tink&amp;rsquo;s open banking integration.</description></item><item><title>@MappedSuperclass: Sharing Fields Without Inheritance Tables</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/mapped-superclass/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/mapped-superclass/</guid><description>Introduction Almost every entity in a real application shares a few common fields: id, createdAt, updatedAt, and perhaps version. Writing these in every entity class is repetitive and error-prone. @MappedSuperclass lets you define them once in a base class that all entities extend — without creating a parent table or any inheritance mapping in the database.
What Is @MappedSuperclass? @MappedSuperclass marks a class whose field mappings are inherited by subclass entities.</description></item><item><title>@Transactional in Depth: Propagation, Isolation, and Rollback</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/transactional-propagation-isolation/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/transactional-propagation-isolation/</guid><description>What @Transactional Does @Transactional tells Spring to wrap the annotated method in a database transaction. Spring opens the transaction before the method starts and commits (or rolls back) when it ends. The annotation works via an AOP proxy — understanding this proxy model is essential for avoiding the most common @Transactional bugs.
The AOP Proxy Model Spring creates a proxy around your bean. When code outside the bean calls a @Transactional method, the call goes through the proxy, which manages the transaction:</description></item><item><title>Auditing: @CreatedDate, @LastModifiedBy, and Hibernate Envers</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/auditing/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/auditing/</guid><description>Why Auditing Matters Production systems must answer: &amp;ldquo;Who changed this? When? What did it look like before?&amp;rdquo; Without auditing infrastructure, you add created_at, updated_at, created_by, updated_by columns manually to every entity — dozens of lines of boilerplate repeated everywhere.
Spring Data JPA auditing fills these fields automatically. Hibernate Envers goes further: it captures every revision of every entity in separate audit tables.
Spring Data JPA Auditing Enable Auditing @Configuration @EnableJpaAuditing(auditorAwareRef = &amp;#34;auditorProvider&amp;#34;) public class JpaAuditingConfig { @Bean public AuditorAware&amp;lt;String&amp;gt; auditorProvider() { return () -&amp;gt; Optional.</description></item><item><title>Basic Entity Mapping: @Entity, @Table, @Id, @Column</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/basic-entity-mapping/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/basic-entity-mapping/</guid><description>Introduction Entity mapping is the process of telling Hibernate how your Java class corresponds to a database table, and how each field maps to a column. JPA provides a rich set of annotations for this — from the minimal @Entity and @Id to the detailed @Column with constraints.
This article covers every annotation and attribute you need to map entities precisely and confidently.
@Entity @Entity marks a class as a JPA entity — a Java object that maps to a database table.</description></item><item><title>Best Practices Reference: The Complete Spring Batch Checklist</title><link>https://blog.devops-monk.com/tutorials/spring-batch/spring-batch-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/spring-batch-best-practices/</guid><description>Introduction This is the final article in the Spring Batch Tutorial series. It consolidates every hard-won lesson into actionable checklists — use it as a review before deploying a new batch job to production, or as a reference when debugging an existing one.
Part 1: Job Design Always design for restartability Every step that writes to a database uses idempotent SQL (ON DUPLICATE KEY UPDATE or WHERE NOT EXISTS) Every ItemReader has a unique .</description></item><item><title>Cascade Types and Orphan Removal: Managing Lifecycle Propagation</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/cascade-types-orphan-removal/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/cascade-types-orphan-removal/</guid><description>Introduction Cascade types control which persistence operations (save, merge, delete, refresh) propagate from a parent entity to its associated children. orphanRemoval controls what happens when a child is removed from the parent&amp;rsquo;s collection. Getting cascade wrong is one of the most common causes of unexpected deletes and data integrity issues in JPA applications.
The Six Cascade Types cascade = CascadeType.PERSIST // propagate persist (save new entity) cascade = CascadeType.MERGE // propagate merge (re-attach detached entity) cascade = CascadeType.</description></item><item><title>Custom Queries with @Query: JPQL and Native SQL</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-queries-jpql-native-sql/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-queries-jpql-native-sql/</guid><description>Why @Query Exists Derived query methods (Article 17) are great for simple conditions, but they break down fast. A method like findByOrderStatusAndCustomerCityAndPriceBetweenOrderByCreatedAtDesc is unreadable and still cannot express a JOIN or aggregation.
@Query lets you write the exact JPQL or SQL you need, attached directly to a repository method — keeping your repository clean while giving you full query control.
JPQL vs Native SQL JPQL Native SQL Language Entity/field names Table/column names Database portable Yes No JOIN FETCH Yes No Window functions No Yes Full-text search No Yes Complex analytics Limited Full power Start with JPQL.</description></item><item><title>Custom Type Conversions: AttributeConverter and Enums</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-type-conversions/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-type-conversions/</guid><description>Introduction JPA handles most Java types automatically — String, Integer, LocalDate, BigDecimal. But what about a Java enum? A List&amp;lt;String&amp;gt; stored as JSON? A custom Money type? JPA&amp;rsquo;s AttributeConverter and @Enumerated let you control exactly how any Java type maps to a database column.
Mapping Enums with @Enumerated Java enums are common in domain models. JPA maps them with @Enumerated:
public enum OrderStatus { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED } public enum ProductStatus { ACTIVE, INACTIVE, DISCONTINUED } @Column(length = 20, nullable = false) @Enumerated(EnumType.</description></item><item><title>Derived Query Methods: Finding Data Without Writing SQL</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/derived-query-methods/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/derived-query-methods/</guid><description>Introduction Spring Data JPA can generate JPQL queries directly from method names. You declare a method like findByStatusAndPriceGreaterThan, and Spring Data parses the name, derives the query, and generates the implementation. No SQL, no @Query, no boilerplate.
How Derived Query Parsing Works Spring Data JPA splits method names into a subject and a predicate:
findBy Email And Status ↑ ↑ ↑ ↑ subject property operator property The subject determines the operation type (find, count, exists, delete).</description></item><item><title>Dirty Checking, Flush Modes, and the First-Level Cache</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/dirty-checking-flush-modes/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/dirty-checking-flush-modes/</guid><description>The Persistence Context Revisited Article 3 introduced the persistence context — the in-memory cache of managed entities. This article goes deeper: how does the persistence context detect changes? When does it flush them to the database? And how can you tune this behaviour?
Dirty Checking When you modify a managed entity, you don&amp;rsquo;t call save() or update(). You simply change the field:
@Transactional public void giveDiscount(Long productId, BigDecimal discount) { Product product = productRepository.</description></item><item><title>Embedded Types and Value Objects: @Embeddable and @Embedded</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/embedded-types-value-objects/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/embedded-types-value-objects/</guid><description>Introduction Not every concept in your domain deserves its own table. An Address — street, city, state, postal code — is a value object: it has no identity of its own, it belongs to an entity. JPA&amp;rsquo;s @Embeddable lets you model this as a separate Java class while storing it in the owning entity&amp;rsquo;s table.
What Is an Embeddable? An @Embeddable class is a Java class whose fields are mapped to columns in the owning entity&amp;rsquo;s table — not a separate table.</description></item><item><title>Entity Graphs and Batch Loading: Precision Fetching</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-graphs-batch-loading/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-graphs-batch-loading/</guid><description>The Problem @EntityGraph Solves JOIN FETCH in @Query solves N+1 but creates inflexibility: the fetch plan is baked into the query. Two different use cases — an order detail page (needs items + customer) and an order list page (needs only customer) — require two different queries with different JOIN FETCHes.
@EntityGraph separates the fetch plan from the query. You define what to load at the call site, and Spring Data JPA generates the appropriate JOIN.</description></item><item><title>Entity Lifecycle States: Transient, Managed, Detached, Removed</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-lifecycle-states/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-lifecycle-states/</guid><description>Introduction Every JPA entity is always in one of four states. The state determines whether Hibernate is tracking the entity, whether changes are detected automatically, and what operations are valid. Understanding these states explains a large class of JPA bugs — especially the dreaded LazyInitializationException and detached entity errors.
The Four States new Customer() │ │ persist / save() ▼ TRANSIENT ──────────────────────► MANAGED (not tracked) (tracked by persistence context) │ │ evict / clear / close ▼ DETACHED (no longer tracked) │ │ merge() ▼ MANAGED (re-attached) MANAGED ─── remove / delete() ──► REMOVED (delete scheduled, still in context) 1.</description></item><item><title>Fetch Types: EAGER vs LAZY Loading</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/fetch-types-eager-vs-lazy/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/fetch-types-eager-vs-lazy/</guid><description>Introduction Every relationship in JPA has a fetch type: either EAGER (load immediately with the parent) or LAZY (load only when accessed). The defaults are counterintuitive, and getting fetch types wrong is one of the top causes of performance problems and LazyInitializationException in JPA applications.
Default Fetch Types Annotation Default fetch type Performance risk @ManyToOne EAGER Loads related entity on every query — can be expensive @OneToOne EAGER Same — loads profile/address on every customer load @OneToMany LAZY Correct default — collections are only loaded when needed @ManyToMany LAZY Correct default The defaults for @ManyToOne and @OneToOne are EAGER — a poor choice that the JPA spec made for historical reasons.</description></item><item><title>Inheritance Strategies: SINGLE_TABLE, JOINED, TABLE_PER_CLASS</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/inheritance-strategies/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/inheritance-strategies/</guid><description>Introduction Object-oriented code uses inheritance to share behaviour. Relational databases have no concept of inheritance. JPA bridges this gap with three strategies for mapping a class hierarchy to tables. Understanding when each is appropriate prevents schema headaches and performance problems.
The Domain Example An e-commerce system has different types of discount:
Discount (abstract) ├── PercentageDiscount (e.g., 10% off) └── FixedAmountDiscount (e.g., $5 off) All discounts share: id, name, validFrom, validUntil. PercentageDiscount adds: percentage (e.</description></item><item><title>Introduction to JPA, Hibernate, and Spring Data JPA</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/introduction-jpa-hibernate-spring-data/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/introduction-jpa-hibernate-spring-data/</guid><description>Introduction Every Spring Boot application that touches a relational database eventually encounters three terms used almost interchangeably: JPA, Hibernate, and Spring Data JPA. They are related but distinct, and understanding the difference is essential before writing a single line of mapping code.
This article explains what each one is, how they fit together, and why this stack is the dominant approach to Java database access.
The Problem: Object-Relational Impedance Mismatch Java applications work with objects: classes, inheritance, collections, references.</description></item><item><title>Many-to-Many Relationships: @ManyToMany and Join Tables</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/many-to-many-relationships/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/many-to-many-relationships/</guid><description>Introduction A many-to-many relationship means records in table A can relate to many records in table B, and vice versa. Products have many tags; tags apply to many products. In SQL this requires a join table. In JPA, @ManyToMany and @JoinTable handle this automatically — but when you need extra data on the join (like a creation date or a relevance score), you need a different approach.
Simple @ManyToMany: Product and Tag products (*) ──── product_tags ──── (*) tags The product_tags join table has two columns: product_id and tag_id.</description></item><item><title>Multi-Threaded Steps and Async Processing for Performance</title><link>https://blog.devops-monk.com/tutorials/spring-batch/multi-threaded-steps/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/multi-threaded-steps/</guid><description>Introduction A single-threaded Spring Batch step processes one chunk at a time — read N items, process N items, write N items, repeat. For large data sets this is a bottleneck. Spring Batch offers two in-JVM scaling options:
Approach How it works Use when Multi-threaded step Multiple threads each process independent chunks Reader is thread-safe (JdbcPagingItemReader) AsyncItemProcessor Processing runs concurrently; writes remain sequential I/O-bound processors (REST calls, slow enrichment) This article covers both, plus the thread-safety requirements you must meet.</description></item><item><title>One-to-Many and Many-to-One: The Most Common Relationship</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-many-many-to-one/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-many-many-to-one/</guid><description>Introduction The one-to-many relationship is the most common in any domain model. An Order has many OrderItems. A Category has many Products. A Customer has many Orders. Understanding @OneToMany and @ManyToOne well — especially bidirectional mapping, collection types, and cascade configuration — is foundational to any JPA application.
The Domain Example orders (1) ──────────────── (*) order_items An order has many order items. Each order item belongs to exactly one order.</description></item><item><title>One-to-One Relationships: @OneToOne in Depth</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-one-relationships/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-one-relationships/</guid><description>Introduction A one-to-one relationship means one record in table A corresponds to exactly one record in table B. In JPA this is modelled with @OneToOne. Understanding where the foreign key lives, which side &amp;ldquo;owns&amp;rdquo; the relationship, and how cascade operations work is essential before moving to the more complex @OneToMany and @ManyToMany.
The Domain Example In the e-commerce system, a Customer has one CustomerProfile containing their preferences and biography. A profile belongs to exactly one customer.</description></item><item><title>Optimistic and Pessimistic Locking: Handling Concurrent Updates</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/optimistic-pessimistic-locking/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/optimistic-pessimistic-locking/</guid><description>The Lost Update Problem Two users edit the same product simultaneously:
Time User A User B T1 Load product (price=999) Load product (price=999) T2 Change price to 899 T3 Change price to 799 T4 Save → UPDATE price=899 T5 Save → UPDATE price=799 User A&amp;rsquo;s change is silently overwritten. This is the lost update problem. Both JPA locking strategies prevent it — in different ways.
Optimistic Locking with @Version Optimistic locking assumes conflicts are rare.</description></item><item><title>Pagination and Sorting with Pageable, Page, and Slice</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/pagination-sorting/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/pagination-sorting/</guid><description>Why Pagination Matters Returning all rows from a database table is one of the most common production incidents. A query that works in development with 100 rows silently becomes a 10-second, 2 GB memory spike when production has 2 million rows. Pagination limits how many rows travel from the database to the application at a time.
Spring Data JPA makes pagination a first-class feature — pass a Pageable to any repository method and get a Page&amp;lt;T&amp;gt; back.</description></item><item><title>Partitioning: Splitting Work Across Parallel Workers</title><link>https://blog.devops-monk.com/tutorials/spring-batch/partitioning/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/partitioning/</guid><description>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&amp;rsquo;s failure doesn&amp;rsquo;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.</description></item><item><title>Performance Tuning: Chunk Size, Connection Pools, and Memory Management</title><link>https://blog.devops-monk.com/tutorials/spring-batch/performance-tuning/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/performance-tuning/</guid><description>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.</description></item><item><title>Primary Keys and Generated Values: IDENTITY, SEQUENCE, UUID</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/primary-keys-generated-values/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/primary-keys-generated-values/</guid><description>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&amp;rsquo;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.</description></item><item><title>Projections and DTOs: Fetching Only What You Need</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/projections-dtos/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/projections-dtos/</guid><description>The Over-Fetching Problem By default, findAll() loads every column for every entity. A Product entity with 20 fields loads all 20 columns — even when a dropdown menu only needs id and name. This wastes bandwidth, memory, and query time.
Projections let you define what shape the result should take, and Spring Data JPA generates a query that fetches only those columns.
Interface Projections The simplest projection: declare an interface with getter methods matching entity field names.</description></item><item><title>Repository Interfaces: CrudRepository, JpaRepository, and How They Work</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/repository-interfaces/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/repository-interfaces/</guid><description>Introduction Spring Data JPA&amp;rsquo;s repository abstraction eliminates the DAO boilerplate that every Java application used to require. You declare an interface, extend one of the repository base interfaces, and Spring generates a complete implementation at startup — find, save, delete, count, and more — with zero code written.
The Repository Hierarchy Repository&amp;lt;T, ID&amp;gt; ← Marker interface — no methods │ └── CrudRepository&amp;lt;T, ID&amp;gt; ← Basic CRUD: save, find, delete, count │ └── PagingAndSortingRepository&amp;lt;T, ID&amp;gt; ← + findAll(Pageable), findAll(Sort) │ └── JpaRepository&amp;lt;T, ID&amp;gt; ← + flush, saveAndFlush, deleteInBatch Each interface adds more operations.</description></item><item><title>Scheduling Batch Jobs: @Scheduled, Quartz, and Clustered Scheduling</title><link>https://blog.devops-monk.com/tutorials/spring-batch/scheduling-batch-jobs/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/scheduling-batch-jobs/</guid><description>Introduction A batch job that only runs manually is barely useful. Production jobs run on a schedule — nightly, hourly, after a file arrives. Spring Boot provides three scheduling options:
Option Persistence Clustered Use when @Scheduled No No Simple cron on a single node Quartz Scheduler Yes (DB) Yes HA scheduling, persistent triggers External scheduler (Kubernetes CronJob, Airflow) Varies Yes Complex pipelines, dependency management @Scheduled — Simple Cron Trigger The simplest approach: annotate a method with @Scheduled and run the job from it.</description></item><item><title>Second-Level Cache and Query Cache with Hibernate</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/second-level-cache/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/second-level-cache/</guid><description>Cache Layers in Hibernate Hibernate has two cache levels:
Level Scope Lifetime Shared? First-level cache One Session (one transaction) Transaction No — per session Second-level cache SessionFactory Application lifetime Yes — across sessions The first-level cache (Article 24) prevents repeated reads within one transaction. The second-level cache prevents repeated reads across transactions — once an entity is loaded, it stays in the shared cache until evicted.
When to Use the Second-Level Cache Good candidates:</description></item><item><title>Setting Up Spring Boot with Spring Data JPA and MySQL</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/setup-spring-boot-spring-data-jpa/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/setup-spring-boot-spring-data-jpa/</guid><description>Introduction This article builds the project foundation used throughout the entire series. By the end, you will have a running Spring Boot 3.3 application connected to MySQL 8.x with Hibernate 6, a proper connection pool, schema management via Flyway, and SQL logging configured so you can see exactly what Hibernate sends to the database.
Project Setup Maven pom.xml &amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt; &amp;lt;project xmlns=&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34; xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34; xsi:schemaLocation=&amp;#34;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&amp;#34;&amp;gt; &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt; &amp;lt;parent&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt; &amp;lt;version&amp;gt;3.</description></item><item><title>Specifications and the Criteria API: Dynamic Queries</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/specifications-criteria-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/specifications-criteria-api/</guid><description>Why Specifications Derived query methods and @Query annotations work for fixed queries. But what happens when the user can filter by any combination of 10 fields — some optional, some not?
// Wrong approach — combinatorial explosion List&amp;lt;Product&amp;gt; findByName(String name); List&amp;lt;Product&amp;gt; findByNameAndPrice(String name, BigDecimal price); List&amp;lt;Product&amp;gt; findByNameAndPriceAndCategory(...); // ... you&amp;#39;d need 2^10 = 1024 methods The Specification pattern solves this. Each filter condition is a reusable Specification&amp;lt;T&amp;gt; object. You compose them with and(), or(), and not() at runtime, and Spring Data JPA generates the correct query.</description></item><item><title>Testing Spring Data JPA: @DataJpaTest, Testcontainers, and Best Practices</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/testing-spring-data-jpa/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/testing-spring-data-jpa/</guid><description>Why Test JPA Code? Unit tests with mocked repositories verify your service logic but nothing about the database. They won&amp;rsquo;t catch:
Wrong column mapping Constraint violations N+1 queries introduced by a new lazy association Transactions that silently don&amp;rsquo;t roll back Queries that fail on the real database but pass on H2 Testing against a real database — or at minimum a database-compatible in-memory store — is necessary. Spring Boot&amp;rsquo;s @DataJpaTest and Testcontainers make this practical.</description></item><item><title>The N+1 Problem: Detection, Root Cause, and All Solutions</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/n-plus-one-problem-solutions/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/n-plus-one-problem-solutions/</guid><description>What Is the N+1 Problem? The N+1 problem occurs when loading a list of N entities triggers N additional queries to load their associations — one query per entity.
Example: load 50 orders, then access each order&amp;rsquo;s customer:
SELECT * FROM orders; -- 1 query SELECT * FROM customers WHERE id = 1; -- query for order 1&amp;#39;s customer SELECT * FROM customers WHERE id = 2; -- query for order 2&amp;#39;s customer SELECT * FROM customers WHERE id = 3; -- query for order 3&amp;#39;s customer .</description></item><item><title>The Persistence Context: How JPA Tracks Your Entities</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/persistence-context-entity-lifecycle/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/persistence-context-entity-lifecycle/</guid><description>Introduction The persistence context is the single most important concept in JPA. Everything else — lazy loading, dirty checking, transaction scope, detached entity exceptions — only makes sense once you understand what the persistence context is and how it works.
Many JPA bugs come from developers not understanding this concept. Understand it well and the rest of JPA becomes predictable.
What Is the Persistence Context? The persistence context is an in-memory map of entities managed by the current EntityManager.</description></item><item><title>Transaction Boundaries and Common Pitfalls</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/transaction-boundaries-pitfalls/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/transaction-boundaries-pitfalls/</guid><description>Transaction Boundaries A transaction boundary is the point where a transaction starts and where it ends. In Spring, boundaries are defined by @Transactional on service methods.
HTTP Request └── Controller (no transaction) └── @Transactional Service method ← transaction OPENS here ├── Repository call 1 ├── Repository call 2 └── method returns ← transaction COMMITS here Everything inside the @Transactional method runs in the same database transaction. The persistence context (first-level cache) lives for the duration of that transaction.</description></item><item><title>Adopting Liquibase on an Existing Production Database</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-existing-database-adoption/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-existing-database-adoption/</guid><description>Most teams don&amp;rsquo;t start with Liquibase. You inherit a production database that&amp;rsquo;s been running for years, modified by scripts, hotfixes, and well-intentioned developers who &amp;ldquo;just ran it manually.&amp;rdquo; Now you want version control. You want repeatable deployments. You want to stop the &amp;ldquo;did you run the migration?&amp;rdquo; Slack message.
The good news: you can adopt Liquibase without touching production data. The bad news: it requires care — not complexity, just care.</description></item><item><title>Advanced Processing: CompositeItemProcessor, External APIs, and Async Processing</title><link>https://blog.devops-monk.com/tutorials/spring-batch/advanced-processors/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/advanced-processors/</guid><description>Introduction Real batch jobs often need more than one transformation step per item. You might validate first, then normalize, then enrich, then convert to the output type. CompositeItemProcessor chains multiple single-responsibility processors together. For I/O-bound enrichment steps, AsyncItemProcessor runs processing concurrently on a thread pool — giving you parallelism without rewriting your step as multi-threaded.
CompositeItemProcessor CompositeItemProcessor chains a list of processors. The output of each processor becomes the input to the next.</description></item><item><title>Advanced Writers: JpaItemWriter, CompositeItemWriter, and Custom Writers</title><link>https://blog.devops-monk.com/tutorials/spring-batch/advanced-writers/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/advanced-writers/</guid><description>Introduction The previous article covered the two most common writers: FlatFileItemWriter for files and JdbcBatchItemWriter for database inserts. This article covers advanced scenarios:
Persisting JPA entities with JpaItemWriter Writing to multiple destinations simultaneously with CompositeItemWriter Routing items to different writers based on content with ClassifierCompositeItemWriter Building custom writers for REST APIs, message queues, and cloud storage Combining a writer with a post-step cleanup tasklet JpaItemWriter JpaItemWriter calls EntityManager.merge() on each item.</description></item><item><title>Best Practices Reference: The Complete Liquibase Checklist</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-best-practices/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-best-practices/</guid><description>Twenty-three articles of Liquibase knowledge, distilled into one reference. Bookmark this page. Use the checklists before every production deployment. Come back when something breaks.
This article doesn&amp;rsquo;t repeat the &amp;ldquo;why&amp;rdquo; — that&amp;rsquo;s in the earlier articles. This is the what: concrete rules, with the article number if you need the full explanation.
The 7 Laws (Non-Negotiable) These apply without exception. Break them and you will eventually have a bad day in production.</description></item><item><title>Changelog Formats: XML, YAML, JSON, and SQL — When to Use Each</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-changelog-formats/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-changelog-formats/</guid><description>Liquibase supports four changelog formats: XML, YAML, JSON, and SQL. The format you pick affects readability, tooling support, and what features are available. This article shows the same changeset in all four formats so you can compare them directly — then explains when each format is the right choice.
The Same Change in All Four Formats To make comparison concrete, here is a single changeset — creating the users table from the e-commerce schema — written in every format.</description></item><item><title>Changelog Organization: Master Files, include/includeAll, Directory Structures</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-changelog-organization/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-changelog-organization/</guid><description>A single changelog file works fine for a tutorial. It stops working the moment two developers add migrations on the same day, or when you need to find the changeset that introduced a column added six months ago, or when a feature branch&amp;rsquo;s migrations must be reviewed independently before merging.
Changelog organization is not a polish step — it is what determines whether Liquibase stays manageable at scale or turns into a source of merge conflicts and mystery failures.</description></item><item><title>Chunk-Oriented Processing: The Core Spring Batch Pattern</title><link>https://blog.devops-monk.com/tutorials/spring-batch/chunk-oriented-processing/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/chunk-oriented-processing/</guid><description>The read-process-write loop is at the heart of almost every Spring Batch job. Understanding exactly how it works — where transactions begin and end, what gets rolled back on failure, and how Spring Batch knows where to restart — makes everything else in the framework click into place.
This article goes deep on chunk-oriented processing: the execution model, the three interfaces, the counters that track progress, and how to size chunks correctly.</description></item><item><title>CI/CD Integration: GitHub Actions Pipeline for Database Deployments</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-cicd-github-actions/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-cicd-github-actions/</guid><description>The Liquibase commands covered in Articles 5 and 16 become reliable only when they run automatically on every change. A developer who remembers to run futureRollbackSQL before merging is better than one who doesn&amp;rsquo;t — but a pipeline that enforces it is better than both.
This article builds a complete GitHub Actions pipeline: PR validation gates that block merges when rollback is missing, a staging deployment workflow, and a production deployment workflow with mandatory tagging and pre-generated rollback files.</description></item><item><title>Common Migration Patterns: 12 Real-World Schema Changes with MySQL</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-migration-patterns/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-migration-patterns/</guid><description>Most Liquibase guides explain change types by listing them. This article is different — it walks through twelve patterns you will encounter repeatedly in a real project, explains the MySQL-specific behaviour of each, and shows the rollback you need to write alongside every change.
All examples build on the ecommerce database established in Part 1. By the end, you will have a complete reference you can reach for on any working day.</description></item><item><title>Configuring Jobs and Steps: Flows, Decisions, and Conditional Execution</title><link>https://blog.devops-monk.com/tutorials/spring-batch/job-step-configuration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/job-step-configuration/</guid><description>Introduction So far every example has had a single step. Real batch jobs usually have multiple steps — validate input, import data, generate a report, send a notification, clean up temp files. Spring Batch&amp;rsquo;s JobBuilder DSL lets you compose these steps into flows with conditional branching, parallel execution, and decision logic.
This article covers:
Linear multi-step jobs Conditional step transitions using ExitStatus JobExecutionDecider for runtime branching Parallel flows with split Nested jobs with FlowStep The Flow abstraction for reusable step sequences Linear Multi-Step Job The simplest multi-step job runs steps in sequence.</description></item><item><title>Contexts and Labels: Multi-Environment Filtering</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-contexts-labels/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-contexts-labels/</guid><description>The most common multi-environment problem in database migrations: seed data that should run in dev and staging but must never touch production. The naive solution is maintaining separate changelogs per environment. The correct solution is contexts and labels — Liquibase&amp;rsquo;s built-in filtering mechanism that lets a single changelog serve every environment.
This article covers both features, explains the critical difference between them, and shows exactly how to wire them into Spring Boot profiles.</description></item><item><title>Core Commands: update, rollback, status, history, validate, diff</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-core-commands/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-core-commands/</guid><description>Liquibase has over 50 commands. In practice, you will use fewer than ten of them for 95% of your work. This article covers those ten commands — what each one does, when to reach for it, and what the output tells you. Every example builds on the ecommerce database and users table from Article 4.
The commands are organized by what you&amp;rsquo;re trying to accomplish: inspect, apply, undo, and verify.</description></item><item><title>Core Concepts: Changelog, Changeset, and Tracking Tables</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-core-concepts/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-core-concepts/</guid><description>Before writing a single migration, you need the mental model. Understanding how Liquibase thinks about changelogs, changesets, and identity prevents the most common mistakes — ones that are painful to fix after deployment.
The Changelog The changelog is the file Liquibase reads. It contains an ordered list of changesets. Think of it as your database&amp;rsquo;s Git history — a sequential record of every change ever made.
# db/changelog/db.changelog-master.yaml databaseChangeLog: - changeSet: id: &amp;#34;20240101-001&amp;#34; author: abhay changes: - createTable: tableName: users columns: - column: name: id type: BIGINT autoIncrement: true constraints: primaryKey: true nullable: false - changeSet: id: &amp;#34;20240101-002&amp;#34; author: abhay changes: - addColumn: tableName: users columns: - column: name: email type: VARCHAR(255) constraints: nullable: false unique: true The changelog format (YAML, XML, SQL) doesn&amp;rsquo;t matter for understanding the concept — it&amp;rsquo;s always a list of changesets in order.</description></item><item><title>Diff, Snapshot, and Reverse Engineering: Onboarding Existing Databases</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-diff-reverse-engineering/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-diff-reverse-engineering/</guid><description>Most Liquibase tutorials start with a blank database. Most real projects start with a production database that has been evolving for years without version control. This article covers the complete workflow for adopting Liquibase on an existing database — generating a baseline changelog, bootstrapping Liquibase tracking, detecting drift between environments, and maintaining snapshots for offline comparisons.
The Onboarding Problem An existing database has no DATABASECHANGELOG table. Running liquibase update with a fresh changelog would try to create tables that already exist, causing immediate failures.</description></item><item><title>Introduction to Spring Batch: What, Why, and Architecture</title><link>https://blog.devops-monk.com/tutorials/spring-batch/introduction-to-spring-batch/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/introduction-to-spring-batch/</guid><description>Every application has a class of work that doesn&amp;rsquo;t fit the request-response model: process 2 million orders overnight, generate 500,000 monthly statements, migrate 10 years of legacy data before Monday morning. This work needs to be reliable, restartable after failures, and fast enough to finish in the available window. That&amp;rsquo;s what Spring Batch is built for.
This article covers what Spring Batch is, when to use it, and how its architecture works.</description></item><item><title>Introduction: Why Database Versioning Matters</title><link>https://blog.devops-monk.com/tutorials/liquibase/introduction-to-liquibase/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/introduction-to-liquibase/</guid><description>Your application code is in Git. Every change is tracked, reviewed, and reversible. Your database schema is not — it lives in someone&amp;rsquo;s head, a shared wiki page, or a folder of SQL scripts with names like fix_final_v3.sql. This is the problem Liquibase solves.
The Problem: Database Drift On a typical team without database versioning:
Developer A adds a column locally and forgets to tell anyone Developer B&amp;rsquo;s tests fail on a table that exists on A&amp;rsquo;s machine but not on B&amp;rsquo;s Staging has 3 extra columns that production doesn&amp;rsquo;t — or vice versa Nobody knows what the &amp;ldquo;correct&amp;rdquo; schema state is Deploying to production means manually running SQL scripts while hoping nothing was missed This is database drift — the schema diverges between environments and nobody tracks when or why.</description></item><item><title>JobParameters, ExecutionContext, and Job Restartability</title><link>https://blog.devops-monk.com/tutorials/spring-batch/job-parameters-execution-context/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/job-parameters-execution-context/</guid><description>Introduction Two mechanisms let you pass information into and through a Spring Batch job:
JobParameters — input values provided at launch time (a date, a file path, a run ID). They are immutable and persisted to BATCH_JOB_EXECUTION_PARAMS. ExecutionContext — a key-value map that steps can read and write during execution. It is persisted after each chunk commit, enabling restartability. Understanding both is essential for building jobs that can be safely re-run, restarted after failure, and parameterised for different data sets.</description></item><item><title>JobRepository and Batch Metadata: How Spring Batch Tracks Everything</title><link>https://blog.devops-monk.com/tutorials/spring-batch/job-repository-batch-metadata/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/job-repository-batch-metadata/</guid><description>Introduction Every time Spring Batch runs a job it records the run&amp;rsquo;s history in a set of relational tables. This metadata is not optional — it is what makes Spring Batch reliable. Without it there would be no restart capability, no duplicate-run prevention, and no audit trail. Understanding the metadata layer is essential for debugging failures, building monitoring dashboards, and designing restartable jobs.
In this article you will learn:
The role of JobRepository and JobExplorer in the Spring Batch architecture The six metadata tables — their schema, purpose, and relationships How Spring Batch uses these tables to enable restartability How to query batch history directly in MySQL How to access metadata programmatically with JobExplorer Key changes in Spring Batch 5 (removed MapJobRepositoryFactoryBean, new testing approach) All examples use Spring Boot 3.</description></item><item><title>Kubernetes Deployments: Jobs, Init Containers, and Helm Hooks</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-kubernetes-deployment/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-kubernetes-deployment/</guid><description>Spring Boot&amp;rsquo;s Liquibase auto-run works fine for a single instance. In Kubernetes, where multiple pods start simultaneously, auto-run at application startup creates a race: every pod acquires DATABASECHANGELOGLOCK, one holds it, the rest wait, and if Kubernetes kills a pod mid-migration (because it failed its readiness probe while waiting on the lock), the lock remains set and blocks every subsequent pod.
The solution is to run migrations exactly once, before application pods start, using either an init container or a Helm pre-upgrade Job.</description></item><item><title>Listeners: Hooking into Job, Step, and Chunk Lifecycle Events</title><link>https://blog.devops-monk.com/tutorials/spring-batch/listeners/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/listeners/</guid><description>Introduction Spring Batch emits lifecycle events at every stage of execution — before and after a job runs, before and after each step, before and after each chunk, and before and after each individual read/process/write call. Listeners let you hook into these events without modifying your core batch logic.
Common uses:
Log start/end times and item counts Send success/failure notifications (Slack, email, PagerDuty) Publish metrics to Prometheus or CloudWatch Log every skipped item to a dead-letter table Reset resources before a step begins Listener Hierarchy Job ├── JobExecutionListener beforeJob / afterJob │ └── Step ├── StepExecutionListener beforeStep / afterStep ├── ChunkListener beforeChunk / afterChunk / afterChunkError ├── ItemReadListener beforeRead / afterRead / onReadError ├── ItemProcessListener beforeProcess / afterProcess / onProcessError └── ItemWriteListener beforeWrite / afterWrite / onWriteError └── SkipListener onSkipInRead / onSkipInWrite / onSkipInProcess JobExecutionListener @Component public class ImportJobListener implements JobExecutionListener { private static final Logger log = LoggerFactory.</description></item><item><title>MySQL-Specific Patterns: Character Sets, Engines, Large Table Migrations</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-mysql-specific-patterns/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-mysql-specific-patterns/</guid><description>Liquibase&amp;rsquo;s change types generate standard SQL. MySQL has quirks — character set requirements that differ from the standard, a storage engine option absent from other databases, row format constraints that affect index key length, and DDL locking behaviour that makes a 10-second table lock acceptable in dev and catastrophic in production.
This article covers the MySQL-specific patterns that you will need once you move from tutorial projects to real production databases.</description></item><item><title>Preconditions: Guard Your Migrations with tableExists, sqlCheck, and More</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-preconditions/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-preconditions/</guid><description>A migration assumes the database is in a specific state. It assumes the table it references exists, the column it modifies is the right type, the user running it has the right privileges, and the database is the right engine. When those assumptions are violated — a hotfix was applied manually, a migration ran out of order, someone ran the wrong changelog against the wrong database — the migration fails in a way that can be hard to diagnose.</description></item><item><title>Property Substitution: Environment-Specific Values in Changelogs</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-property-substitution/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-property-substitution/</guid><description>A changeset that hard-codes the schema name ecommerce works in production but breaks when your staging database is called ecommerce_staging. A changeset that seeds a specific admin email works in dev but shouldn&amp;rsquo;t run with the same value in staging. Property substitution lets you parameterize these values so one changelog serves every environment.
How Property Substitution Works Liquibase replaces ${property-name} tokens in your changelog with the value assigned to that property before executing any SQL.</description></item><item><title>Reading Flat Files: CSV, Fixed-Width, and Delimited with FlatFileItemReader</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-flat-files/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-flat-files/</guid><description>Introduction Flat files — CSV exports, fixed-width mainframe feeds, pipe-delimited data dumps — are the most common input source for batch jobs. Spring Batch&amp;rsquo;s FlatFileItemReader handles all of them. It is restartable out of the box: it persists its line-number position in the ExecutionContext so that a restarted job resumes exactly where it crashed.
In this article you will build a complete order-import job that reads a CSV file and inserts rows into a MySQL orders table.</description></item><item><title>Reading from External Sources: REST APIs, S3, and Custom ItemReaders</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-external-sources/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-external-sources/</guid><description>Introduction Not all batch input comes from files or databases. You may need to pull orders from an e-commerce API, sync products from a supplier feed, or process CSV exports stored in Amazon S3. Spring Batch provides MultiResourceItemReader for S3 files and a clean ItemReader interface for anything else.
In this article you will build:
A custom ItemReader that pages through a REST API An S3 reader that downloads files on demand A composite reader that merges multiple sources into one step The ItemReader Contract The entire ItemReader interface is one method:</description></item><item><title>Reading from MySQL: JdbcCursorItemReader and JdbcPagingItemReader</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-from-mysql/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-from-mysql/</guid><description>Introduction Most real batch jobs read from a database, not a file. Spring Batch provides two JDBC readers for this:
Reader Strategy Thread-safe Use when JdbcCursorItemReader Open a server-side cursor, stream rows No Single-threaded step, huge result sets JdbcPagingItemReader Execute LIMIT / OFFSET queries in a loop Yes Multi-threaded steps, sorted data Both handle restartability automatically and both work with any DataSource — including MySQL.
JdbcCursorItemReader How it works The reader opens a single JDBC ResultSet on Step.</description></item><item><title>Reading with JPA: JpaPagingItemReader and Entity-Based Reading</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-with-jpa/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-with-jpa/</guid><description>Introduction When your application already uses JPA/Hibernate, JpaPagingItemReader lets you read data using JPQL queries and mapped entities instead of raw JDBC. You get the full object graph, type-safe queries, and familiar entity lifecycle — but you also inherit JPA&amp;rsquo;s pitfalls: the N+1 problem, session-per-read overhead, and first-level cache growth.
This article covers:
When to choose JpaPagingItemReader over JdbcPagingItemReader Setting up the reader with JPQL and named queries Fetching associations to avoid N+1 Clearing the persistence context to prevent memory leaks A complete order-processing example with MySQL When to Use JpaPagingItemReader Use it when:</description></item><item><title>Retry Logic: Handling Transient Failures Gracefully</title><link>https://blog.devops-monk.com/tutorials/spring-batch/retry-logic/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/retry-logic/</guid><description>Introduction Batch jobs interact with databases, REST APIs, and file systems — all of which fail transiently. A MySQL deadlock resolves itself in milliseconds. A network timeout to an external service clears up in seconds. Retrying these transient failures automatically is far better than failing the entire job and requiring a manual restart.
Spring Batch has built-in retry support at the step level, integrated with its transaction management. This article covers everything you need to configure robust retry behaviour.</description></item><item><title>Rollback in Production: Tag-Based Strategies and CI Validation</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-production-rollback/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-production-rollback/</guid><description>Article 9 covered what rollback commands exist. This article covers how to make rollback reliable in production — where a slow or broken rollback costs real money and real users. The difference between knowing the commands and having a working rollback strategy is process: mandatory tagging, pre-generated rollback files, CI gates, and a decision tree that removes guesswork during an incident.
Why Rollback Fails in Production Rollback failures in production share a small set of root causes:</description></item><item><title>Rollback Strategies: Automatic, Custom, Tag-Based, and Count-Based</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-rollback-strategies/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-rollback-strategies/</guid><description>Rollback is the thing you build at 2am when the deployment broke production. The problem is that 2am is exactly the wrong time to discover your rollback blocks are missing, incomplete, or untested.
This article covers rollback from a strategy perspective: how Liquibase generates rollback, when you must write it yourself, how to handle change types that cannot be rolled back at all, and how to validate your rollback strategy in CI so it works when you need it.</description></item><item><title>Skip Logic, Dead Letter Patterns, and Job Restart Strategies</title><link>https://blog.devops-monk.com/tutorials/spring-batch/skip-logic-restartability/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/skip-logic-restartability/</guid><description>Introduction Retry handles transient failures. Skip handles permanent ones — bad data rows, constraint violations, malformed records that will never succeed no matter how many times you retry. Skip logic lets your job continue processing good records while recording bad ones for human review.
This article covers:
Configuring skip for specific exception types Custom SkipPolicy for fine-grained control Dead-letter table pattern for tracking skipped items Stopping a job intentionally vs failing it Handling abandoned executions Designing jobs that restart safely after any failure Basic Skip Configuration return new StepBuilder(&amp;#34;importOrdersStep&amp;#34;, jobRepository) .</description></item><item><title>Spring Boot + Spring Batch Setup: Your First Complete Batch Project</title><link>https://blog.devops-monk.com/tutorials/spring-batch/spring-boot-spring-batch-setup/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/spring-boot-spring-batch-setup/</guid><description>Article 2 showed how chunk processing works conceptually. This article sets up the real project you&amp;rsquo;ll build on for the rest of the series — complete Maven configuration, MySQL metadata tables, multiple ways to trigger jobs, and correct use of JobParameters. Everything in this article is production-ready from the start.
Project Dependencies Add spring-boot-starter-batch to your Maven project. That one starter brings in everything you need.
&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt; &amp;lt;project xmlns=&amp;#34;http://maven.</description></item><item><title>Spring Boot Integration: Zero-Config Setup and Full Properties Reference</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-spring-boot-integration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-spring-boot-integration/</guid><description>Running Liquibase from the CLI is fine for learning and for standalone scripts. In a real Spring Boot application, you want migrations to run automatically at startup — before the application code touches the database. Spring Boot&amp;rsquo;s Liquibase auto-configuration handles this with zero boilerplate: add the dependency, point to your changelog, and migrations run before the application context is ready.
This article covers the complete integration: Maven/Gradle setup, how auto-run works and why, the full spring.</description></item><item><title>Stored Procedures, Views, and Triggers in MySQL</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-stored-objects-mysql/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-stored-objects-mysql/</guid><description>Tables and columns are straightforward in Liquibase — one change type per operation, automatic rollback, clean history. Stored procedures, views, and triggers are different. They contain body SQL that uses semicolons internally, which conflicts with Liquibase&amp;rsquo;s default statement splitting. They are replaced rather than altered. And unlike tables, modifying them frequently over time is expected.
This article covers the mechanics and patterns that make stored objects manageable in Liquibase.
The Core Problem: Delimiter Conflicts A stored procedure body contains semicolons to terminate each statement inside it:</description></item><item><title>Tasklets: Running Non-Chunk Operations in Batch Jobs</title><link>https://blog.devops-monk.com/tutorials/spring-batch/tasklets/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/tasklets/</guid><description>Introduction Not every step in a batch job reads-processes-writes a stream of items. Sometimes you need to:
Delete yesterday&amp;rsquo;s temp files before reading today&amp;rsquo;s data Truncate a staging table before loading new data Call a stored procedure to aggregate results Send an email or Slack notification after processing Execute a DDL statement to add an index These operations do not have items — they are single units of work. Spring Batch handles them with the Tasklet interface.</description></item><item><title>Team Collaboration: Naming Conventions, Conflict Prevention, Git Workflow</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-team-collaboration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-team-collaboration/</guid><description>A solo developer can treat Liquibase conventions as suggestions. A team of five cannot. When two developers both write &amp;ldquo;the next migration&amp;rdquo; on the same day, without a convention that prevents collision, you get duplicate IDs, merge conflicts on the master changelog, or — worst case — two changesets that silently overwrite each other in DATABASECHANGELOG.
This article is the operating agreement for teams using Liquibase: what conventions to establish, how to structure the git workflow, what the PR review checklist looks like, and what to do when conflicts happen despite the conventions.</description></item><item><title>Testing Migrations: H2 vs Testcontainers vs Real MySQL</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-testing-migrations/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-testing-migrations/</guid><description>The CI pipeline from Article 19 validates migrations against a MySQL service container. But that&amp;rsquo;s not the same as testing that your application code works correctly after the migration runs. This article covers how to structure Spring Boot tests that validate both the migration and the application behaviour — and makes the case for replacing H2 with Testcontainers as your primary testing database.
Three Testing Strategies Strategy Database Speed Fidelity Use For H2 in-memory H2 (in-memory) Fastest Low — MySQL-specific SQL fails Unit tests that mock repositories H2 with MySQL mode H2 (MySQL compat) Fast Medium — most SQL works, some quirks Basic integration tests on simple schemas Testcontainers Real MySQL 8.</description></item><item><title>Testing Spring Batch Jobs: Unit Tests, Integration Tests, and @SpringBatchTest</title><link>https://blog.devops-monk.com/tutorials/spring-batch/testing-spring-batch/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/testing-spring-batch/</guid><description>Introduction Spring Batch jobs are code — they need tests. Without tests you will catch errors in production. With tests you catch them in CI.
Spring Batch has a dedicated testing module that provides @SpringBatchTest, JobLauncherTestUtils, and JobRepositoryTestUtils. These tools let you:
Run a complete job in a test and assert on its BatchStatus Launch individual steps and inspect StepExecution counters Test readers, processors, and writers in complete isolation This article covers all three levels: unit, integration, and database integration with Testcontainers.</description></item><item><title>Transforming Data with ItemProcessor: Validation, Filtering, and Enrichment</title><link>https://blog.devops-monk.com/tutorials/spring-batch/item-processor/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/item-processor/</guid><description>Introduction ItemProcessor&amp;lt;I, O&amp;gt; sits between the reader and the writer. It receives one item at a time and returns a transformed item — or null to filter the item out entirely. Processors are optional: if your job reads and writes the same type with no transformation needed, you can omit them.
The processor interface is one method:
public interface ItemProcessor&amp;lt;I, O&amp;gt; { O process(I item) throws Exception; } Return a processed item to pass it to the writer.</description></item><item><title>Troubleshooting: 10 Common Liquibase Errors and How to Fix Them</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-troubleshooting/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-troubleshooting/</guid><description>Liquibase errors tend to cluster around the same ten problems. You will hit most of them at least once. This article gives you the exact error message to search for, the root cause, and the fastest fix — so you spend minutes recovering, not hours debugging.
Error 1: DATABASECHANGELOGLOCK — &amp;ldquo;Waiting for changelog lock&amp;rdquo; Symptoms Waiting for changelog lock.... Waiting for changelog lock.... Waiting for changelog lock.... liquibase.exception.LockException: Could not acquire change log lock.</description></item><item><title>Writing to Files and Databases: FlatFileItemWriter and JdbcBatchItemWriter</title><link>https://blog.devops-monk.com/tutorials/spring-batch/writing-files-databases/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/writing-files-databases/</guid><description>Introduction After reading and processing data, your batch job needs to write results somewhere. Spring Batch provides two essential writers:
FlatFileItemWriter — writes items to CSV or any delimited/formatted flat file JdbcBatchItemWriter — writes items to a database using JDBC batch inserts/updates Both are transactional and restartable. This article covers both in depth, plus transaction semantics you must understand to avoid duplicates and partial writes.
How Writers Work in Spring Batch Writers receive a Chunk&amp;lt;O&amp;gt; — a list of all items processed in the current transaction.</description></item><item><title>Your First Migration: MySQL Setup and Running liquibase update</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-first-migration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-first-migration/</guid><description>You&amp;rsquo;ve read about changesets, tracking tables, and changelog formats. Now you&amp;rsquo;re going to run your first real migration against a live MySQL database. By the end of this article you will have connected Liquibase to MySQL, written a changelog that creates the users table for our e-commerce app, previewed the SQL it generates, applied it, and verified the result in the database.
This is the article where things become real.</description></item><item><title>Zero-Downtime Deployments: The Expand-Contract Pattern</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-zero-downtime-deployments/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-zero-downtime-deployments/</guid><description>A RENAME COLUMN statement takes milliseconds. But if your application is still running the old code when the rename executes, every query that uses the old column name fails immediately. Zero-downtime schema changes are not about making DDL faster — they are about sequencing changes so that no single step breaks the running application.
The expand-contract pattern is the standard solution. It breaks dangerous migrations into safe, incremental phases that allow old and new application code to coexist with the same database during deployment.</description></item></channel></rss>