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:
Continue reading »Mysql
80 posts in this section
Setting Up Spring Boot with Spring Data JPA and MySQL
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 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.
Continue reading »Specifications and the Criteria API: Dynamic Queries
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<Product> findByName(String name); List<Product> findByNameAndPrice(String name, BigDecimal price); List<Product> findByNameAndPriceAndCategory(...); // ... you'd need 2^10 = 1024 methods The Specification pattern solves this. Each filter condition is a reusable Specification<T> object. You compose them with and(), or(), and not() at runtime, and Spring Data JPA generates the correct query.
Continue reading »Testing Spring Data JPA: @DataJpaTest, Testcontainers, and Best Practices
Why Test JPA Code? Unit tests with mocked repositories verify your service logic but nothing about the database. They won’t catch: Wrong column mapping Constraint violations N+1 queries introduced by a new lazy association Transactions that silently don’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’s @DataJpaTest and Testcontainers make this practical.
Continue reading »The N+1 Problem: Detection, Root Cause, and All Solutions
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’s customer: SELECT * FROM orders; -- 1 query SELECT * FROM customers WHERE id = 1; -- query for order 1's customer SELECT * FROM customers WHERE id = 2; -- query for order 2's customer SELECT * FROM customers WHERE id = 3; -- query for order 3's customer .
Continue reading »The Persistence Context: How JPA Tracks Your Entities
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.
Continue reading »Transaction Boundaries and Common Pitfalls
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.
Continue reading »Adopting Liquibase on an Existing Production Database
Most teams don’t start with Liquibase. You inherit a production database that’s been running for years, modified by scripts, hotfixes, and well-intentioned developers who “just ran it manually.” Now you want version control. You want repeatable deployments. You want to stop the “did you run the migration?” Slack message. The good news: you can adopt Liquibase without touching production data. The bad news: it requires care — not complexity, just care.
Continue reading »Advanced Processing: CompositeItemProcessor, External APIs, and Async Processing
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.
Continue reading »Advanced Writers: JpaItemWriter, CompositeItemWriter, and Custom Writers
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.
Continue reading »