Skip to main content

equals() and hashCode() in Hibernate: Five Patterns That Look Right and Aren’t

A team added a Product to a HashSet, called em.persist(product), and then checked whether the set contained it. The answer was false. The entity was in the database. It was in the set before the persist call. After the call it had effectively vanished from the set, even though nobody removed it. The bug is the database-generated ID in hashCode(). Before persist, id is null; hashCode() returns some value based on null. After persist, id is 42; hashCode() returns a completely different value. The entity is now sitting in the wrong bucket of the HashSet. contains() looks in the right bucket, finds nothing, returns false. The entity is lost — not from the database, just from the set — and every operation that relied on that set produces wrong answers. This is pattern one. There are four more, each equally invisible until the wrong moment. Each one looks like a reasonable implementation when you read it. The problem only shows up at runtime, often under specific conditions that don't reproduce in unit tests.

Master Java Bean Validation with Hibernate Validator 7: A Complete Guide

Have you ever spent hours debugging a "NullPointerException" or a corrupted database entry only to realize the data was malformed from the start? Manually writing if (user.getName() == null) checks across your entire service layer is not just tedious—it's a maintenance nightmare that leads to "spaghetti code." In the world of modern Java development, ensuring data integrity is paramount. This is where Java Bean Validation with Hibernate Validator 7 comes into play, providing a robust, annotation-based framework to handle constraints elegantly. In this guide, we’ll dive deep into how Hibernate Validator (the reference implementation of Jakarta Bean Validation) works with Hibernate 7 to keep your data clean and your code concise. The Problem: The "Validation Logic" Chaos Imagine a registration form for a high-traffic application. You need to ensure: The username isn't empty. The email is valid and follows global standards. The age is at least 18. The password meets complex security requirements (uppercase, digits, special characters). Without a centralized system, you end up duplicating this logic in your REST controllers, your service layer, and sometimes even at the database level using DDL constraints. This inconsistency leads to "Validation Drift," where one part of the app accepts data that another part rejects. This results in inconsistent system states, obscure runtime errors, and a poor user experience. The Agitation: Maintenance Debt and Data Corruption As your application grows, managing manual validation becomes a massive bottleneck. If the business requirement for a "valid phone number" changes to include international formats, you have to find every single if statement across dozens of files. Miss one? You’ve just introduced a security vulnerability or a data integrity issue that could poison your database for months.

Master Hibernate 7 Connection Pooling with HikariCP: The Definitive Performance Guide

Are you struggling with sluggish database response times or "Connection is closed" exceptions in your Java logs? In modern enterprise applications, your database connection pool is the heart of your infrastructure. If that heart beats too slowly, your entire system suffers. In this guide, we explore how to integrate Hibernate 7 with HikariCP—the "zero-overhead" connection pool—to achieve maximum throughput and reliability. The Problem: The Latency of Connection Handshakes Database connections are heavy. When Hibernate needs to execute a query without a pool, it must perform a series of time-consuming steps: Open a network socket to the DB server. Complete a TCP/IP handshake. Negotiate SSL/TLS security. Authenticate the database user. Under high load, these milliseconds accumulate, leading to massive latency spikes. Without a pool, your application spends more time "connecting" than actually "querying." The Agitation: Why "Old School" Pools are Falling Behind For years, developers relied on pools like c3p0 or DBCP. While reliable, these libraries were built for an era of lower concurrency. They often suffer from: Internal Locking: Threads frequently block each other just to "borrow" a connection. Complexity: Dozens of confusing parameters that lead to misconfiguration. Size: Heavyweight codebases that increase your application's memory footprint. The Solution: Hibernate 7 + HikariCP HikariCP is widely recognized as the fastest connection pool available for the JVM. It is built on highly optimized, lock-free data structures and micro-benchmarked to ensure near-zero overhead.

Mastering Hibernate 6 L2 Caching with Ehcache 3: The Definitive Guide

Hibernate 6 introduced a major architectural shift. By moving to the Jakarta EE namespace and embracing the JCache (JSR-107) standard, it changed how we interact with caching providers. If you are using Hibernate 6, the legacy hibernate-ehcache dependency is dead. To achieve high-performance data access today, you need the modern JCache bridge. The Problem: Database Bottlenecks in Hibernate 6 Even with the performance improvements in Hibernate 6’s new SQM (Semantic Query Model), database latency remains the primary bottleneck for scaling. Without a Second Level (L2) Cache, every time a new Session (EntityManager) is opened—even for the same user—Hibernate is forced to hit the database for data that hasn't changed. This results in redundant SQL SELECT statements, higher DB CPU usage, and increased costs in cloud environments where you pay for IOPS and database instance sizing. The Solution: Hibernate 6 + Ehcache 3 (JSR-107) The modern solution for Hibernate 6 is to use Ehcache 3 as a JCache provider. This allows Hibernate to offload entity and collection state to memory, sharing it across all sessions in the SessionFactory. Prerequisites Java 11+: Hibernate 6 requires a minimum of Java 11 (it is Hibernate 7 that raises the baseline to Java 17). Jakarta Persistence 3.x: The modern jakarta.persistence namespace. Step 1: Hibernate 6 Dependencies In Hibernate 6, you must use the hibernate-jcache module. Crucially, your Ehcache dependency must include the jakarta classifier to avoid namespace conflicts.

Master Hibernate 7 Ehcache 3 Configuration: High-Performance Caching with Jakarta Persistence

Are you struggling with database performance in your modern Java applications? As systems scale, database bottlenecks remain the primary cause of latency. While Hibernate 7 introduces massive improvements in query generation and Jakarta Persistence compatibility, the secret to true high-concurrency performance lies in the Second-Level (L2) Cache. In this guide, we will configure Ehcache 3 — the industry standard for JVM-level caching — as the L2 cache provider for Hibernate 7, using the modern JCache (JSR-107) bridge.

Hibernate 7 Second-Level Cache: When to Turn It On, How to Configure Ehcache 3, and the Three Ways It Will Stale Your Data

The question that matters before enabling L2 cache is not "can I make it faster" but "can I tolerate stale data, and for how long". The cache sits between Hibernate and the database, serving data that may have been written by a different JVM process, a batch job, a DBA running a script, or another application instance. Every one of those paths can invalidate the cache without Hibernate knowing. If your answer to the staleness question is "no, I cannot tolerate any staleness" — skip L2 entirely. If the answer is "yes, but only for these entities and within these bounds" — read on. This post covers what L2 actually stores, how to configure Ehcache 3 as a JCache provider, what changed between Hibernate 6 and 7, the three specific staleness modes that catch production teams off guard, and why the query cache is almost always the wrong additional layer.

The Hibernate First-Level Cache Explained (It’s Not What You Think It Is)

Most developers who have used Hibernate for any length of time know the first-level cache exists. Ask them to describe it and you'll hear something like: "It's a cache Hibernate uses so it doesn't hit the database twice for the same row." That's technically correct, but it misses almost everything that matters. The first-level cache is not a feature you enable, a setting you tune, or an optional layer you bolt on for performance. It is the persistence context itself — always-on, transaction-scoped by default in Spring, and the thing that makes dirty checking, identity guarantees, and cascade operations possible. If you have ever used em.find(), you have used it. If you have ever hit an OutOfMemoryError in a batch job that loaded 200,000 entities, the first-level cache is why. This post is a deep look at the mechanics: what the persistence context stores, when it is consulted, how EntityKey works, and the failure modes that catch experienced developers off guard.

Mastering Stored Procedures with Hibernate 7: A Deep Dive for High-Performance Java Apps

If you’ve ever written nested loops in Java just to process thousands of records—only to watch latency skyrocket—you’re not alone. This application–database “chattiness” is a silent performance killer that creeps into enterprise systems as they scale. Every time your application fetches a row, transforms it in memory, and sends it back to the server, you incur the cumulative overhead of network round-trips, intensive object-relational mapping (ORM) overhead, and heavy JVM garbage collection cycles. For a few dozen records, this is negligible; for a few million, it is a catastrophic bottleneck that can bring a production environment to its knees. The solution? Shift data‑intensive logic into the database layer and invoke it through Hibernate 7. In this guide, you’ll learn when and how to use stored procedures safely, portably, and efficiently. Why Use Stored Procedures in Hibernate 7? Hibernate 7 continues to improve support for native database features while aligning fully with Jakarta Persistence 3.2. Stored procedures are not a silver bullet, but in the right scenarios they offer tangible benefits: Reduce Network Latency: Execute complex logic in a single round-trip instead of hundreds of individual queries. Centralize Logic: Keep data-heavy calculations close to the data to avoid serialization overhead. Security: Expose only procedures instead of granting direct table access. Traffic Optimization: Offload bulk relational work to the database engine, which is optimized for it. Note: Hibernate also provides the native ProcedureCall API for finer-grained control. For most portable applications, prefer the JPA-standard StoredProcedureQuery. The PAS Framework: Problem, Agitation, Solution The Problem Your application must calculate a year-end bonus based on multiple tables: Sales, Attendance, Tenure, and departmental performance.

The Ultimate Guide to Hibernate 7 Criteria Queries: Master Dynamic, Type-Safe Persistence

Writing dynamic queries with string-based HQL is fragile. One typo, one missing space, and your application fails at runtime. In enterprise applications—where filters change based on user input—this often turns into a mess of concatenated strings that are hard to test and harder to maintain. Hibernate 7’s Criteria API solves this by letting you build queries programmatically using a type-safe, refactoring-friendly API. With alignment to Jakarta Persistence 3.2+ and Hibernate 7’s improved Semantic Query Model (SQM), Criteria queries are now more predictable and production-ready than ever. In this guide, we’ll walk through Criteria Queries step by step—from basic selection to joins, analytics, and bulk operations—so you can confidently use them for real-world, dynamic data access. Why the Criteria API Exists In a typical enterprise application, search filters are rarely static. Users toggle checkboxes, select ranges, and combine conditions. Expressing this logic with raw HQL usually results in brittle string concatenation, runtime-only failures, and code that becomes impossible to refactor safely. The Criteria API replaces query strings with a structured, object-based model. Instead of writing text, you assemble query parts using Java objects. This shifts many errors from runtime to compile time and makes your persistence layer safer and easier to evolve.

The Ultimate Guide to Hibernate Query Language (HQL) in Hibernate 7

Hibernate Query Language (HQL) lets you query your domain model instead of raw database tables, eliminating brittle SQL strings and manual result mapping. With the Semantic Query Model (SQM) engine (introduced in Hibernate 6, refined in 7), HQL is now more type-safe, more predictable, and better optimized than ever. In this guide, you'll learn how to use HQL effectively — covering pagination, joins, aggregates, and the most common pitfalls developers face moving from SQL to HQL.