Tag Archives: Performance

Performance — performance optimization, benchmarking, and profiling in Java applications

Why Your JUnit Tests Are Slow (Performance Optimization Guide)

A test suite that takes 20 minutes to run does not get run. Developers skip it, work around it, or disable it entirely. Speed is not a nice-to-have in automated testing — it is fundamental to whether your tests actually provide value. This guide diagnoses every common cause of slow JUnit 6 tests and provides concrete, measurable optimisations with real performance numbers.

Measuring First: Identify Your Bottlenecks

Before optimising anything, measure. You need to know which tests are slowest before you can improve them.

# Maven: run tests and capture timing in the Surefire report
mvn test
# Check: target/surefire-reports/*.xml — each test has 'time' attribute

# Find the slowest tests across all XML reports
grep -h 'time=' target/surefire-reports/*.xml | 
  grep -oP 'time="[0-9.]+"' | 
  sort -t'"' -k2 -rn | 
  head -20

# Gradle: generate a test report with timing
./gradlew test
# Check: build/reports/tests/test/index.html
# Sort by duration in the 'Classes' tab

Problem 1: Spring Context Starts on Every Test Class

Starting a Spring ApplicationContext takes 3–10 seconds. If 20 test classes each trigger a fresh context, that is 60–200 seconds of pure startup overhead before a single assertion runs.

// SLOW: Different @MockBean in each class = unique context = fresh startup each time
@SpringBootTest
class OrderControllerTest {
    @MockBean PaymentGateway gateway;  // unique context config A
}

@SpringBootTest
class InvoiceControllerTest {
    @MockBean TaxCalculator taxCalc;   // unique context config B = ANOTHER startup
}

// FAST: Shared context configuration = Spring caches and reuses it
// Define all @MockBeans in ONE base class — all subclasses share the same context
@SpringBootTest
@MockBeanAnnotationsFromClass  // or simply put all @MockBeans here
public abstract class BaseIntegrationTest {
    // Declare ALL mocks used by ANY integration test here
    @MockBean protected PaymentGateway paymentGateway;
    @MockBean protected TaxCalculator  taxCalculator;
    @MockBean protected EmailService   emailService;
    // Spring starts ONE context for all subclasses = startup cost paid ONCE
}

class OrderControllerTest extends BaseIntegrationTest { /* tests */ }
class InvoiceControllerTest extends BaseIntegrationTest { /* tests */ }
// Context reused — 3-10 second startup paid ONCE instead of per class
Continue reading Why Your JUnit Tests Are Slow (Performance Optimization Guide)

Parallel Test Execution in JUnit 6: Configuration and Pitfalls

Running tests sequentially is safe but slow. A test suite that takes 10 minutes to run sequentially can often complete in 2–3 minutes with parallel execution. JUnit 6 has first-class support for parallel test execution — but it requires careful configuration to avoid flaky tests caused by shared state. This guide covers every configuration option, pitfall, and real-world pattern you need to enable parallel execution confidently.

How JUnit 6 Parallel Execution Works

JUnit 6 parallel execution is disabled by default. When enabled, JUnit can run tests at two levels:

  • Class-level parallelism — multiple test classes run concurrently in different threads
  • Method-level parallelism — multiple test methods within the same class run concurrently

These are configured independently, giving you fine-grained control over the degree of parallelism.

Step 1: Enable Parallel Execution

Create or edit src/test/resources/junit-platform.properties:

# Enable parallel execution (disabled by default)
junit.jupiter.execution.parallel.enabled=true

# Default execution mode for all tests
# 'concurrent' = run in parallel with other tests at the same level
# 'same_thread' = run in the same thread as parent (sequential)
junit.jupiter.execution.parallel.mode.default=concurrent

# Default execution mode for top-level test classes
# concurrent = classes run in parallel
# same_thread = classes run sequentially (but methods within might be concurrent)
junit.jupiter.execution.parallel.mode.classes.default=concurrent

Step 2: Configure the Thread Pool Strategy

# Strategy: 'dynamic' uses (availableProcessors * factor) threads
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.config.dynamic.factor=1.0

# Strategy: 'fixed' uses an exact thread count
# junit.jupiter.execution.parallel.config.strategy=fixed
# junit.jupiter.execution.parallel.config.fixed.parallelism=4

# Strategy: 'custom' uses your own ParallelExecutionConfigurationStrategy
# junit.jupiter.execution.parallel.config.strategy=custom
# junit.jupiter.execution.parallel.config.custom.class=com.example.MyStrategy
Continue reading Parallel Test Execution in JUnit 6: Configuration and Pitfalls

Mastering Hibernate 7 Aggregate Functions: The Ultimate Guide for High-Performance Data Retrieval

Are you tired of pulling massive lists of entities into your Java application just to calculate a simple total or average? Data bottlenecks are the silent killers of enterprise applications. When you fetch thousands of rows only to perform math in memory, you aren’t just wasting CPU cycles — you’re suffocating your database and increasing latency.

In modern development with Hibernate 7, leveraging aggregate functions is the solution that transforms sluggish data processing into lightning-fast database-level operations. By using COUNT, SUM, AVG, MIN, and MAX, you delegate the heavy lifting to the database engine, ensuring your application remains lean and responsive.

Continue reading Mastering Hibernate 7 Aggregate Functions: The Ultimate Guide for High-Performance Data Retrieval

Master Hibernate 7 Pagination: The Ultimate Guide for High-Performance Java Apps

When your database grows from hundreds to millions of records, fetching everything in a single query isn’t just slow—it’s a recipe for an OutOfMemoryError. Whether you are building a modern dashboard or a high-traffic e-commerce site, Hibernate 7 pagination is the essential technique to keep your application responsive and your memory footprint low. In this guide, we’ll explore how to implement efficient pagination using the latest Jakarta Persistence (JPA) standards.

The Problem: The “Data Avalanche”

Imagine a user searching for products on your site. If your backend attempts to load 50,000 rows into memory just to display the first 10, the server will lag, the database will lock up, and the user will likely bounce before the page even loads. For high-concurrency systems, even a few unoptimized queries can saturate the database connection pool, leading to a total system outage.

The Agitation: Why “Limit 10” Isn’t Enough

Many developers treat pagination as an afterthought, adding a simple limit at the end of a query. However, inefficient queries lead to high CPU usage and increased cloud infrastructure costs. Without a structured approach to Hibernate pagination, you risk:

  1. The N+1 Problem: Accidentally triggering thousands of extra queries for related data while trying to paginate the main list.
  2. Broken Sort Orders: Unpredictable result sets when new data is inserted between page loads.
  3. The Memory Trap: Fetching millions of rows into the application layer just to discard 99% of them in Java code.

If your application can’t scale its data delivery, it can’t scale its user base.

Continue reading Master Hibernate 7 Pagination: The Ultimate Guide for High-Performance Java Apps

Hibernate 7 Batch Insert: 1M Rows in 12s — Settings, Benchmarks, and the JDBC Trap

One million rows, PostgreSQL, HikariCP pool of 10, SEQUENCE generator: approximately 12 seconds with proper Hibernate batch configuration. Without it, the same job runs for over two minutes — and that’s before accounting for the OutOfMemoryError you get around row 80,000 if you skip the flush/clear cycle.

The configuration is four properties. The JDBC trap is one silent gotcha that disables batching without any error. The flush/clear cadence is one loop pattern. This post covers all three and the approximate numbers so you can reason about what your specific job should take.

The Problem: The “Chatty” Database Trap

When you persist objects in a standard loop, Hibernate sends one INSERT or UPDATE statement per object. This creates massive network latency. Imagine you have 10,000 records and a 5ms network round-trip delay. You have already lost 50 seconds just to “talk” to the database, even before the engine starts processing the data.

This is often called the “N+1 Problem of Writing.” Each individual insert requires the database to parse the SQL, execute it, update indexes, and send an acknowledgment. Multiplying this overhead by thousands of records is the fastest way to kill application performance.

Continue reading Hibernate 7 Batch Insert: 1M Rows in 12s — Settings, Benchmarks, and the JDBC Trap

Mastering Hibernate Search 7: Bring Modern Full-Text Search to Your Java Applications

Have you ever noticed how users abandon applications when the search bar feels “broken”? If your app relies on basic SQL LIKE %keyword% queries, you’re likely frustrating your users with slow results, lack of typo tolerance, and irrelevant matches. In the modern web, a subpar search experience is a silent killer for user retention.

Hibernate Search 7 is the widely adopted solution to this problem. By synchronizing your database entities with powerful search engines like Apache Lucene or Elasticsearch, it allows you to implement complex full-text search capabilities with just a few annotations. In this guide, we will explore how to integrate Hibernate Search 7 into your project to provide fast, relevant, and “intelligent” search results.

This guide is intended for Java developers already using Hibernate ORM who want to implement production-grade full-text search without manually managing Elasticsearch or Lucene.

The Problem: Why Traditional SQL Search Fails

Standard relational databases are built for structured data retrieval—finding an exact ID or a specific category. When you try to perform “fuzzy” searches (e.g., searching for “Hiberante” and expecting “Hibernate”), SQL falls short.

  • Performance: LIKE '%keyword%' queries are notoriously slow because they cannot use standard B-Tree indexes. They force the database to perform a full table scan, which might work for 1,000 rows but will crawl to a halt at 1,000,000.
  • Relevance (Scoring): SQL treats every match as a binary “yes” or “no.” It doesn’t understand that a keyword appearing in the Title should rank higher than a keyword appearing in the Footer.
  • Language Nuance: SQL doesn’t know that “running,” “runs,” and “ran” are all variations of the word “run.” This process, known as stemming, is a core feature of dedicated search engines.
Continue reading Mastering Hibernate Search 7: Bring Modern Full-Text Search to Your Java Applications

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:

  1. Open a network socket to the DB server.
  2. Complete a TCP/IP handshake.
  3. Negotiate SSL/TLS security.
  4. 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.

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