In this tutorial, we’ll learn how to integrate Redis into a Jakarta EE application using the Lettuce client library. Redis is a powerful in-memory data structure store, often used as a cache, message broker, or database. Lettuce is a popular Java client for Redis, providing both synchronous and asynchronous capabilities. We will use a simple REST service to interact with Redis, demonstrating how to add and retrieve data from a Redis hash.
Prerequisites
- Basic knowledge of Jakarta EE and Java.
- A Jakarta EE-compatible application server (e.g., WildFly).
- Redis installed and running locally, or a Docker environment to launch it in a Container
Introduction to Redis and Lettuce
What is Redis?
Redis is an in-memory data structure store that can be used as a cache, message broker, and database. It supports data structures like strings, hashes, lists, sets, and more. Due to its in-memory nature, Redis provides very high read and write throughput, making it suitable for use cases where performance is critical.
What is Lettuce?
Lettuce is a scalable, thread-safe, and fully non-blocking Redis client for Java. It supports synchronous, asynchronous, and reactive programming models. Lettuce makes it easy to connect to a Redis server and perform various operations using a simple API.
Step 1: Start Redis
There are several options to get started with Redis. Check the documentation for more details. Here, we will bootstrap Redis as Container image with the following command:
docker run -d --name my-redis-stack -p 6379:6379 redis/redis-stack-server:latest
Then, verify with the command docker ps that Redis is up and running:
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ba00bb835999 redis/redis-stack-server:latest "/entrypoint.sh" About an hour ago Up About an hour 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp my-redis-stack
Step 2: Setting Up the Jakarta EE Project
- Create a Jakarta EE project in your favorite IDE (e.g., IntelliJ, Eclipse). Ensure that the project is set up with the appropriate Jakarta EE libraries.
- Add Lettuce as a Dependency. If you are using Maven, add the following dependency to your
pom.xml:
<dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>6.3.2.RELEASE</version> <!-- Check for the latest version on Maven Central --> </dependency>
Step 3: Setting Up Redis in Jakarta EE
Firstly, we will use a RedisClient to connect to Redis. In Jakarta EE, we can use CDI (Contexts and Dependency Injection) to produce the RedisClient and inject it wherever needed.
import io.lettuce.core.RedisClient;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Disposes;
import jakarta.enterprise.inject.Produces;
@ApplicationScoped
public class RedisClientProducer {
// Producer method for RedisClient
@Produces
@ApplicationScoped
public RedisClient createRedisClient() {
return RedisClient.create("redis://localhost:6379");
}
// Disposer method to close the RedisClient
public void closeRedisClient(@Disposes RedisClient redisClient) {
redisClient.shutdown();
}
}
With the above Producer, we are able to inject an instance of RedisClient in our Jakarta EE application. Also, as we can see from Lettuce documentation, the instance of RedisClient is thread-safe so we can share it with multiple Clients.
Step 4: Implementing the REST Service
Let’s create a REST service with two endpoints:
GET /redis– Retrieve all entries from a Redis Hash.POST /redis– Add a new entry to the Redis Hash.
@Path("/redis")
public class SimpleRedisService {
@Inject
RedisClient redisClient;
private static final String HASH_KEY = "user-session:123";
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllEntries() {
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisAsyncCommands<String, String> asyncCommands = connection.async();
// Get all key-value pairs in the hash
Map<String, String> result = asyncCommands.hgetall(HASH_KEY).get();
// Convert the result to a JSON string using JSON-B
try (Jsonb jsonb = JsonbBuilder.create()) {
String jsonResult = jsonb.toJson(result);
return Response.ok(jsonResult).build();
}
} catch (ExecutionException | InterruptedException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Error retrieving entries: " + e.getMessage())
.build();
} catch (Exception e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Failed to convert to JSON: " + e.getMessage())
.build();
}
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response addEntry(Map<String, String> entry) {
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisAsyncCommands<String, String> asyncCommands = connection.async();
// Add the new key-value pair to the hash
asyncCommands.hset(HASH_KEY, entry).get();
// Respond with the updated hash
Map<String, String> updatedResult = asyncCommands.hgetall(HASH_KEY).get();
try (Jsonb jsonb = JsonbBuilder.create()) {
String jsonResult = jsonb.toJson(updatedResult);
return Response.ok(jsonResult).build();
}
} catch (ExecutionException | InterruptedException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Error adding entry: " + e.getMessage())
.build();
} catch (Exception e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Failed to convert to JSON: " + e.getMessage())
.build();
}
}
}
In the provided example, the Redis Hash type is used. Here’s a brief overview of why hashes are employed and how they function in this context:
Redis Hash Overview
- Data Structure: A Redis hash is a collection of key-value pairs, similar to a dictionary or map in programming languages. Each hash can store multiple fields, making it suitable for representing structured data.
- Use Case in the Example: In the
SimpleRediservice, we use a Redis hash to store user session data. The hash key is defined as"user-session:123", and it holds multiple fields such asname,surname,company, andagewith their corresponding values.

Step 5: Deploy and Test the Application
- Deploy the application to your Jakarta EE application server (e.g., WildFly).
- Test the endpoints using
curlor a tool like Postman.
Example curl Commands:
- POST Request to add an Entry:
curl -X POST http://localhost:8080/rest-demo/redis \
-H "Content-Type: application/json" \
-d '{"key": "email", "value": "[email protected]"}'
GET Request to list Entries:
curl http://localhost:8080/rest-demo/redis | jq
And here is the output:
{
"surname": "Smith",
"name": "John",
"company": "Redis",
"age": "29",
"value": "[email protected]",
"key": "email"
}
Summary
In this tutorial, we’ve learned how to:
- Set up a Jakarta EE application to use Redis via the Lettuce client.
- Implement a simple RESTful service with
GETandPOSTmethods for adding and retrieving data from Redis. - Use JSON-B for serializing and deserializing data.
This basic example can be expanded further to include more complex Redis operations, error handling, and application-specific logic.
Source code: https://github.com/fmarchioni/mastertheboss/tree/master/various/redis