Skip to main content

Spring

Distributed Caching in Spring Boot: Hazelcast vs Infinispan vs Redis

Spring's cache abstraction makes a specific promise: annotate a method with @Cacheable, and the business code never knows or cares which product is actually storing the cached value. This post puts that promise to the test with three genuinely different backends — Hazelcast and Infinispan, both embedded in-process, and Redis, a real out-of-process server — running the exact same service class and the exact same test logic against each, swapped only by which CacheManager bean wins. The interesting part turned out not to be the happy path, which is identical across all three, but three real failures along the way: a missing transitive dependency that breaks startup regardless of which backend you asked for, a JMX collision between embedded cache managers sharing one test JVM, and one backend that — unlike the other two — refuses to create a cache you never declared. Versions used in this post. Spring Boot 4.1.1 · com.hazelcast:hazelcast-spring 5.5.0 · org.infinispan:infinispan-spring-boot4-starter-embedded 16.1.4 · Redis server 8.8.3 (Testcontainers) · JDK 25 (Temurin) · Testcontainers 2.0.5. All three resolved and verified via mvn dependency:tree and javap against the real jars — none of the class names or artifact coordinates below were assumed from documentation.

Spring Data Redis JsonOperations: Partial Reads and Writes on JSON Documents

Most Redis client code still treats a value as an opaque blob: read the whole thing, deserialize it, change one field in Java, serialize it, write the whole thing back. Spring Data Redis's new JsonOperations skips that round trip entirely — it talks the RedisJSON module's own commands (JSON.SET, JSON.GET, JSON.ARRAPPEND, ...), which read and mutate a document inside Redis, server-side, addressed by a JSONPath expression. You can fetch one field, append to a nested array, or read a computed slice of a document without ever pulling the rest of it over the wire. This post builds a small shopping-cart service around it: a whole-document save and read, a single-field path read, an array append that never touches the rest of the document, and a delete — each backed by a real Redis 8 container and a passing test. Versions used in this post. Spring Boot 4.1.1 · Spring Data Redis 4.2.0-M2 (milestone — see below) · Jackson 2 jackson-databind (added explicitly) · Redis server 8.8.3 · JDK 25 (Temurin) · Testcontainers 2.0.5. JsonOperations/RedisJsonTemplate does not exist at all in the 4.1.1 GA line that Spring Boot 4.1.1's own BOM manages — confirmed by inspecting the resolved 4.1.1 jar with javap, not assumed from docs. It ships first in the 4.2 milestone train, so this post pins that one artifact to 4.2.0-M2 on top of an otherwise-ordinary Boot 4.1.1 project. That override is the actual subject of this post, not an oversight. This demo is small enough that it doesn't warrant its own repository module. Every source file is linked for download right where it's discussed below, and the full set is also uploaded to this site's media library if you'd rather grab everything at once.

Full-Text Search with Spring Data Elasticsearch on Spring Boot 4

Spring Data Elasticsearch's ElasticsearchRepository looks exactly like JpaRepository — derived query methods, @Id, the same muscle memory. That similarity hides the one decision that actually matters in Elasticsearch: how each field is mapped. Map a field "text" and it's tokenized, stemmed and fuzzy-matchable; map it "keyword" and it's an exact, unanalyzed string good for filtering, sorting and aggregating. Pick wrong and nothing throws an error — the field just quietly stops doing what you expect. This post builds a small product-search service on Spring Boot 4.1 and Elasticsearch 9.4 — a repository for the simple lookups, and the query DSL for everything a repository method can't express: multi-field relevance scoring with per-field boosting, typo-tolerant fuzzy matching, a price filter that narrows results without touching their rank, and a terms aggregation for category facet counts. Versions used in this post. Spring Boot 4.1.1 · Spring Data Elasticsearch 6.1.1 · elasticsearch-java client 9.4.5 · Elasticsearch server 9.4.8 · JDK 25 (Temurin) · Testcontainers 2.0.5 (testcontainers-elasticsearch). The server version is pinned to match the client's minor version — confirmed via mvn dependency:tree rather than assumed, after discovering elsewhere in this series that a mismatched tag can be silently broken upstream.

Spring Boot 4 with MongoDB: Transactions, Aggregations and Indexes That Actually Work

Spring Data MongoDB makes a document database feel so much like a relational one — repositories, derived query methods, @Id — that it's easy to forget the two places where the resemblance stops being helpful: transactions and indexes. Both are opt-in, both fail silently when you skip them, and both are exactly the kind of thing a tutorial glosses over because the happy path doesn't need them. This post builds a small order-placement service on Spring Boot 4.1 and MongoDB 9.0 — repositories for the simple lookups, an aggregation pipeline for a report no repository method can express, a multi-document transaction across two collections, and a compound index whose absence is provable with explain(), not just asserted in prose. Versions used in this post. Spring Boot 4.1.1 · Spring Data MongoDB (via spring-data-bom 2026.0.1) · MongoDB Java driver 5.8.1 · MongoDB server 9.0.2 · JDK 25 (Temurin) · Testcontainers 2.0.5 (testcontainers-mongodb). Run against a single-node MongoDB replica set in Docker — Testcontainers' MongoDBContainer configures replica-set mode automatically on this version; there is no .withReplicaSet() call to make, unlike what older tutorials show.

Time-Series Data in Java: QuestDB vs InfluxDB vs TimescaleDB

The same 100,000-row sensor workload, ingested and downsampled identically on QuestDB, InfluxDB 3 Core, and TimescaleDB — with real benchmark numbers and a real Arrow Flight / Netty bug in the official InfluxDB 3 Java client, reproduced and fixed.

Background Jobs in Spring Boot 4 with JobRunr 9

Fire-and-forget, scheduled and recurring jobs with retries, a dashboard and durable steps, run on JobRunr 9.0.1 and Spring Boot 4.1.1. Leads with the three ways it fails to start, shows a job surviving a restart, and compares it honestly with @Async and @Scheduled.