Author: Brian Sam-Bodden
Add a JSON-backed domain model to Redi2Read using the RedisJSON Redis module.
In this lesson, you'll learn how to:
- Use JSON documents as extensions of your domain.
- Implement a custom Spring Repository to work with JSON documents.
- Implement the
CartControllerusing the JRedisJSON client library. If you get stuck:
- The progress made in this lesson is available on the redi2read github repository at https://github.com/redis-developer/redi2read/tree/course/milestone-6
We will implement the
CartItem models backed by a custom Spring Repository that uses the RedisJSON API via the JRedisJSON client library.
We will represent a user’s cart as a JSON document containing cart item subdocuments. As you can see in the class diagram, a
Cart has zero or more
CartItems, and it belongs to a
RedisJSON is a Redis module that lets you store, update, and fetch JSON values natively in Redis. JSON can be a better fit for modeling complex data in Redis than Hashes because, unlike Hashes, JSON values can contain nested arrays and objects.
JRedisJSON (https://github.com/RedisJSON/JRedisJSON) is a Java client that provides access to RedisJSON's Redis API and provides Java serialization using Google’s GSON library.
We will use a SNAPSHOT version of JRedisJSON to take advantage of more advanced JSON manipulation features recently introduced. Add the snapshots-repo to your Maven POM:
We’ll start with the
CartItem model. It holds information about a
Book in the
Cart; it stores the
Book ISBN (id), price, and quantity added to the cart.
Add the file
src/main/java/com/redislabs/edu/redi2read/models/CartItem.java with the following contents:
The Cart model contains the ID of the owning User and a set of
CartItems. Utility methods exist to return the total number of items in the cart and the total cost.
Add the file
src/main/java/com/redislabs/edu/redi2read/models/Cart.java with the following contents:
After a user checks out, we need to keep track of the books the user now owns. To keep it simple,
we will add a
Set<Book> to the
User model annotated with the
We’ll also include a utility method that adds books to the user’s collection of books owned.
Make the changes below to the User model:
@Reference annotation works for our
Sets in the context of Redis serialization, but you might have noticed that
the roles were being fully serialized into the resulting JSON payload by Jackson.
We will add the
@JsonIdentityReference with the
alwaysAsId parameter set to
true, which, given the proper meta-information in the target classes (
Role), will make Jackson serialize collections of these objects as IDs.
@JsonIdentityInfo annotation allows us to set a generator (
ObjectIdGenerator.PropertyGenerator) using the
id property to direct how the serialization will happen in the presence of the
Add the annotation to the
Book model as shown:
Similarly, we’ll add the
@JsonIdentityInfo to the
Now, when JSON serialization occurs in the REST controllers, the user collection will include the roles as JSON arrays of role IDs. The user collection will also include the newly added collection of books as an array of book IDs.
RedisJSON is not yet seamlessly integrated with Spring, but that does not prevent us from using RedisJSON the “Spring Way”. We have provided an implementation of Spring’s CrudRepository so that we can implement our services and controllers. Add the file src/main/java/com/redislabs/edu/redi2read/repositories/CartRepository.java with the following contents:
As with the
@RedisHash annotated entities, our Carts are maintained with a collection of Redis JSON objects and a Redis Set to maintain the collection of keys.
As Spring applications get more complex, using the repositories directly on your controllers overcomplicates the controllers and diverts from the responsibility to control
routing and deal with incoming parameters and outgoing JSON payloads.
An approach to keep both models and controllers from getting bloated with business logic (“fat” models and “fat” controllers) is to introduce a business logic service layer.
We’ll do so for the cart business logic by introducing the
CartService introduces four cart-related business methods:
get: Finds a cart by id
addToCart: Adds a cart item to a cart
removeFromCart: Removes an isbn from the cart’s set of cart items
checkout: Given a cart ID, adds the contents to the user’s collection of owned books
Add the file
src/main/java/com/redislabs/edu/redi2read/services/CartService.java with the following contents:
The service implements the
removeFromCart methods natively at the JSON level using JSONPath syntax to add and remove items from the cart.
Let’s delve deeper into the implementation of these methods.
In the addToCart method:
- We search for the cart by ID
- If we find the card, then we search for the book to be added to the cart by ISBN using the
- If we find the book, we add the book’s current price to the item (we don’t want customers setting their own prices)
- We then use the JSON.ARRAPPEND command to insert the JSON object into the JSON array at the JSONPath expression
In the removeFromCart method: We search for the cart by ID.
- If we find the cart, we search for the index of the item to be removed in the array of cart items.
- If we find the item, we use the JSON.ARRPOP command to remove the item by its index at the JSONPath expression “.cartItems”.
We now have all the pieces in place to create a
CommandLineRunner that can generate random carts for our users.
As done previously, we will set the number of carts generated using an application property. To do so, add the following to the file
CommandLineRunner is shown below. Add it to the boot package.
Let’s break down the CreateCarts class:
- As with other
CommandLineRunners, we check that there are no carts created.
- For each cart to be created, we
- Retrieve a random user.
- Create a cart for the user.
- Retrieve between 1 and 7 books.
- Add the cart items to the cart for the retrieved books.
- Randomly “checkout” the cart.
There are two private utility methods at the bottom of the class to get a random number of books and to create cart items from a set of books. Upon server start (after some CPU cycles) you should see:
We can now use the Redis CLI to get a random cart key from the cart set, check the type of one of the keys (ReJSON-RL) and use the JSON.GET command to retrieve the JSON payload:
CartController is mostly a pass-through to the
CartService (as controllers are intended to be).
Let’s use curl to request a cart by its ID:
Which should return a payload like: