Java Records: A WebFlux and Spring Data Example

When defining classes for a simple aggregation of values, Java developers have traditionally relied on constructors, accessors, equals(), hashCode() and toString(), an error-prone practice that has low value and shifts the focus away from modeling immutable data. Java records were introduced as a first preview in JDK 14 in order to simplify how we write data carrier classes. The second preview came in JDK 15 and the finalized feature arrived in JDK 16. A summary of this history is available in the JDK Enhancement Proposal JEP 395.

While code generators can be used to reduce boilerplate code, the goals of the record proposals focus on its semantics. In this post, let’s explore Java records’ features and advantages, and apply them for building a REST API and querying a database.

OpenJDK Logo

Prerequisites:

Table of Contents

The record keyword

The record is a new type of declaration, a restricted form of class that acts as a transparent carrier for immutable data. Let’s start the exploration by defining a simple data type EndOfGame as a record:

import java.time.LocalDate;
import java.time.LocalTime;

public record EndOfGame(String id, LocalDate date, LocalTime timeOfDay,
                        String mentalState, Integer damageTaken,
                        Integer damageToPlayers, Integer damageToStructures) {
}

As you can see in the example above, the record has a name, EndOfGame in this case. What looks like a constructor signature is the state description or header, declaring the components of the record.

The following members are acquired automatically with the declaration:

  • A private final field and a public read accessor for each component of the state description
  • A public canonical constructor with the same signature as the state description, which initializes each field from the corresponding argument
  • Implementation of equals(), hashCode() and toString()

The tests in the EndOfGameTest class below verify that the automatic members are indeed available:

package com.okta.developer.records.domain;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDate;
import java.time.LocalTime;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class EndOfGameTest {

    private static final Logger logger = LoggerFactory.getLogger(EndOfGameTest.class);

    private EndOfGame createEndOfGame() {

        return new EndOfGame("1", LocalDate.of(2018, 12, 12),
                LocalTime.of(15, 15), "sober",
                10, 10, 10);
    }

    @Test
    public void equalsTest() {
        EndOfGame eog1 = createEndOfGame();
        EndOfGame eog2 = createEndOfGame();

        assertTrue(eog1.equals(eog2));
        assertEquals(eog1, eog2);
        assertEquals(eog1.hashCode(), eog2.hashCode());
    }

    @Test
    public void toStringTest() {
        EndOfGame eog = createEndOfGame();
        logger.info(eog.toString());

        assertEquals("EndOfGame[id=1, date=2018-12-12, timeOfDay=15:15, mentalState=sober, " +
                        "damageTaken=10, damageToPlayers=10, damageToStructures=10]",
                eog.toString());
    }

    @Test
    public void accessorTest() {
        EndOfGame eog = createEndOfGame();
        assertEquals("sober", eog.mentalState());
    }

}

The automatic canonical constructor is used for creating a sample EndOfGame in the method createEndOfGame(). In the equalsTest() above, eog1 has the same state as eog2, so eog1.equals(eog2) is true. This also implies both instances have the same hashCode. Automatic read accessors have the same name and return type as the component. Note there is no get* prefix in the read accessor name, which is the same name as the component, as illustrated in the accessorTest().

Java record restrictions and rules

While record provides a more concise syntax and semantics designed to help to model data aggregates, as stated before, a record is a restricted form of a class. Let’s have a brief look at those restrictions.

Inheritance, extensibility, and immutability

A record is implicitly final, and cannot be abstract. You cannot enhance it later by extension, as the compiler will output the following error:

Cannot inherit from final 'com.okta.developer.records.EndOfGame'

A record cannot extend any class, not even its implicit superclass Record. But it can implement interfaces. Using the extends with records clause will cause the following error:

No extends clause allowed for record

A record does not have write accessors and the implicitly declared fields are final, and not modifiable via reflection. Moreover, a record cannot declare instance fields outside the record header. Records embody an immutable by default policy, usually applied to data carrier classes.

Instance field is not allowed in record
Cannot assign a value to final variable 'id'

Read accessors can be declared explicitly, but should never silently alter the record state. Review record semantics before making explicit declarations of automatic members.

A record without any constructor declaration will be given a canonical constructor with the same signature as the header, which will assign all the private fields from the arguments. The canonical constructor can be declared explicitly in the standard syntax, or in a compact form:

public EndOfGame {
    Objects.requireNonNull(date);
    Objects.requireNonNull(timeOfDay);
    Objects.requireNonNull(mentalState);
    Objects.requireNonNull(damageTaken);
    Objects.requireNonNull(damageToPlayers);
    Objects.requireNonNull(damageToStructures);
}

In the compact form, the parameters are implicit, the private fields cannot be assigned inside the body, and are automatically assigned at the end. This enables a focus on validation, making defensive copies of mutable values, or some other value processing.

Serialization, encoding, and mapping

Record instances can extend Serializable, but the serialization and deserialization processes cannot be customized. Serialization and deserialization methods like writeObject, readObject can be implemented, but will be ignored.

As spring-web module provides Jackson with JSON encoders and decoders, and Java record support was added to Jackson in release 2.12, records can be used for REST API request and response mapping.

Records cannot be used as entities with JPA/Hibernate. JPA entities must have a no-args constructor, and must not be final, two requirements that record will not support. There are additional issues with JPA and records too.

Spring Data modules that do not use the object mapping of the underlying data store (like JPA) support records, as persistence construction detection works as with other classes. For Spring Data MongoDB, the general recommendation is to stick to immutable objects, because they are straightforward to materialize by calling the constructor. An all-args constructor, allows materialization to skip property population for optimal performance. This too is recommended, and Java record semantics align with these guidelines.

Use Java records with Spring WebFlux and Spring Data

While searching for a dataset for this tutorial, I came across a collection of 87 end game statistics from a single player, for a popular game. As the author included his mental state in the game play data, I decided to use Java records for building a basic average query and finding out if the player’s performance was significantly altered when sober vs when intoxicated.

Let’s jump ahead and build a secured REST Api using the EndOfGame record, Spring Boot, MongoDB and Okta authentication. With the help of Spring Initializr create a WebFlux application, from the web UI or with HTTPie:

https -d start.spring.io/starter.zip bootVersion==2.5.6 \
  baseDir==java-records \
  groupId==com.okta.developer.records \
  artifactId==records-demo \
  name==java-records \
  packageName==com.okta.developer.records \
  javaVersion==17 \
  dependencies==webflux,okta,data-mongodb-reactive

Note: Although Java records have been available since release 14, the Spring Initializr Web UI only lets you select Java Long Term Support (LTS) releases.

Extract the Maven project, and edit pom.xml to add two more required dependencies for this tutorial, MongoDB Testcontainers Module for testing database access, and spring-security-test:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>mongodb</artifactId>
    <version>1.15.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>5.5.1</version>
    <scope>test</scope>
</dependency>

Before you begin, you’ll need a free Okta developer account. Install the Okta CLI and run okta register to sign up for a new account. If you already have an account, run okta login. Then, run okta apps create. Select the default app name, or change it as you see fit. Choose Web and press Enter.

Select Okta Spring Boot Starter. Accept the default Redirect URI values provided for you. That is, a Login Redirect of http://localhost:8080/login/oauth2/code/okta and a Logout Redirect of http://localhost:8080.

The Okta CLI will create an OIDC Web App in your Okta Org. It will add the redirect URIs you specified and grant access to the Everyone group. You will see output like the following when it’s finished:

Okta application configuration has been written to: 
  /path/to/app/src/main/resources/application.properties

Open src/main/resources/application.properties to see the issuer and credentials for your app.

okta.oauth2.issuer=https://dev-133337.okta.com/oauth2/default
okta.oauth2.client-id=0oab8eb55Kb9jdMIr5d6
okta.oauth2.client-secret=NEVER-SHOW-SECRETS

NOTE: You can also use the Okta Admin Console to create your app. See Create a Spring Boot App for more information.

Rename application.properties to application.yml, and add the following content:

okta:
  oauth2:
    issuer: https://{yourOktaDomain}/oauth2/default</span>
<span class="na">client-id</span><span class="pi">:</span> <span class="pi">{</span><span class="nv">clientId</span><span class="pi">}</span>
<span class="na">client-secret</span><span class="pi">:</span> <span class="pi">{</span><span class="nv">clientSecret</span><span class="pi">}</span>
<span class="na">spring</span><span class="pi">:</span>
<span class="na">data</span><span class="pi">:</span>
<span class="na">mongodb</span><span class="pi">:</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">27017</span>
<span class="na">database</span><span class="pi">:</span> <span class="s">fortnite</span>
<span class="na">logging</span><span class="pi">:</span>
<span class="na">level</span><span class="pi">:</span>
<span class="s">org.springframework.data.mongodb</span><span class="pi">:</span> <span class="s">TRACE</span>
</code></pre></div></div>
<p>Add an <code class="language-plaintext highlighter-rouge">EndOfGame</code> record under the package <code class="language-plaintext highlighter-rouge">com.okta.developer.records.domain</code>. Annotate the <code class="language-plaintext highlighter-rouge">record</code> with <code class="language-plaintext highlighter-rouge">@Document(collection = "stats")</code> to let MongoDB map <code class="language-plaintext highlighter-rouge">EndOfGame</code> to the <code class="language-plaintext highlighter-rouge">stats</code> collection. As you can see, a record class can be annotated. The class should look like this:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.domain</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.mongodb.core.mapping.Document</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalDate</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalTime</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Objects</span><span class="o">;</span>
<span class="nd">@Document</span><span class="o">(</span><span class="n">collection</span> <span class="o">=</span> <span class="s">"stats"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">record</span> <span class="nf">EndOfGame</span><span class="o">(</span><span class="nc">String</span> <span class="n">id</span><span class="o">,</span> <span class="nc">LocalDate</span> <span class="n">date</span><span class="o">,</span> <span class="nc">LocalTime</span> <span class="n">timeOfDay</span><span class="o">,</span>
<span class="nc">String</span> <span class="n">mentalState</span><span class="o">,</span> <span class="nc">Integer</span> <span class="n">damageTaken</span><span class="o">,</span>
<span class="nc">Integer</span> <span class="n">damageToPlayers</span><span class="o">,</span> <span class="nc">Integer</span> <span class="n">damageToStructures</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">public</span> <span class="nc">EndOfGame</span> <span class="o">{</span>
<span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">date</span><span class="o">);</span>
<span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">timeOfDay</span><span class="o">);</span>
<span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">mentalState</span><span class="o">);</span>
<span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">damageTaken</span><span class="o">);</span>
<span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">damageToPlayers</span><span class="o">);</span>
<span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">damageToStructures</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Add a new record for the mental state query <code class="language-plaintext highlighter-rouge">MentalStateDamage</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.domain</span><span class="o">;</span>
<span class="kd">public</span> <span class="n">record</span> <span class="nf">MentalStateDamage</span><span class="o">(</span><span class="nc">String</span> <span class="n">mentalState</span><span class="o">,</span>
<span class="nc">Double</span> <span class="n">damageToPlayers</span><span class="o">,</span>
<span class="nc">Double</span> <span class="n">damageToStructures</span><span class="o">,</span>
<span class="nc">Double</span> <span class="n">damageTaken</span><span class="o">)</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Create the package <code class="language-plaintext highlighter-rouge">com.okta.developer.records.repository</code> and add a <code class="language-plaintext highlighter-rouge">MentalStateStatsRepository</code> interface:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.MentalStateDamage</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">MentalStateStatsRepository</span> <span class="o">{</span>
<span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">MentalStateDamage</span><span class="o">&gt;</span> <span class="nf">queryMentalStateAverageDamage</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Add the implementation <code class="language-plaintext highlighter-rouge">MentalStateStatsRepositoryImpl</code> to retrieve the average damage in each category, for each mental state:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.EndOfGame</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.MentalStateDamage</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.mongodb.core.ReactiveMongoTemplate</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.mongodb.core.aggregation.Aggregation</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">data</span><span class="o">.</span><span class="na">mongodb</span><span class="o">.</span><span class="na">core</span><span class="o">.</span><span class="na">aggregation</span><span class="o">.</span><span class="na">Aggregation</span><span class="o">.*;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MentalStateStatsRepositoryImpl</span> <span class="kd">implements</span> <span class="nc">MentalStateStatsRepository</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">ReactiveMongoTemplate</span> <span class="n">mongoTemplate</span><span class="o">;</span>
<span class="nd">@Autowired</span>
<span class="kd">public</span> <span class="nf">MentalStateStatsRepositoryImpl</span><span class="o">(</span><span class="nc">ReactiveMongoTemplate</span> <span class="n">mongoTemplate</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">mongoTemplate</span> <span class="o">=</span> <span class="n">mongoTemplate</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">MentalStateDamage</span><span class="o">&gt;</span> <span class="nf">queryMentalStateAverageDamage</span><span class="o">()</span> <span class="o">{</span>
<span class="nc">Aggregation</span> <span class="n">aggregation</span> <span class="o">=</span> <span class="n">newAggregation</span><span class="o">(</span>
<span class="n">group</span><span class="o">(</span><span class="s">"mentalState"</span><span class="o">)</span>
<span class="o">.</span><span class="na">first</span><span class="o">(</span><span class="s">"mentalState"</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="s">"mentalState"</span><span class="o">)</span>
<span class="o">.</span><span class="na">avg</span><span class="o">(</span><span class="s">"damageToPlayers"</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="s">"damageToPlayers"</span><span class="o">)</span>
<span class="o">.</span><span class="na">avg</span><span class="o">(</span><span class="s">"damageTaken"</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="s">"damageTaken"</span><span class="o">)</span>
<span class="o">.</span><span class="na">avg</span><span class="o">(</span><span class="s">"damageToStructures"</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="s">"damageToStructures"</span><span class="o">),</span>
<span class="n">project</span><span class="o">(</span><span class="s">"mentalState"</span><span class="o">,</span> <span class="s">"damageToPlayers"</span><span class="o">,</span> <span class="s">"damageTaken"</span><span class="o">,</span> <span class="s">"damageToStructures"</span><span class="o">)</span>
<span class="o">);</span>
<span class="k">return</span> <span class="n">mongoTemplate</span><span class="o">.</span><span class="na">aggregate</span><span class="o">(</span><span class="n">aggregation</span><span class="o">,</span> <span class="nc">EndOfGame</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="nc">MentalStateDamage</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>Note</strong>: The <code class="language-plaintext highlighter-rouge">Impl</code> suffix is required for customizing individual repositories with <a href="https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.single-repository-behavior">Spring Data</a></p>
<p>Create the <code class="language-plaintext highlighter-rouge">StatsRepository</code> interface, extending the <code class="language-plaintext highlighter-rouge">MentalStateStatsRepository</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.EndOfGame</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.repository.reactive.ReactiveSortingRepository</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">StatsRepository</span> <span class="kd">extends</span> <span class="nc">ReactiveSortingRepository</span><span class="o">&lt;</span><span class="nc">EndOfGame</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;,</span> <span class="nc">MentalStateStatsRepository</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>
<p>After the import,the sample dataset, will create strings for the date and time values, in a custom format. Create the following converters to map <code class="language-plaintext highlighter-rouge">String</code> to <code class="language-plaintext highlighter-rouge">LocalDate</code> and <code class="language-plaintext highlighter-rouge">LocalTime</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.core.convert.converter.Converter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalDate</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">LocalDateConverter</span> <span class="kd">implements</span> <span class="nc">Converter</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">LocalDate</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">LocalDate</span> <span class="nf">convert</span><span class="o">(</span><span class="nc">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">s</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.core.convert.converter.Converter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalTime</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">LocalTimeConverter</span> <span class="kd">implements</span> <span class="nc">Converter</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">LocalTime</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">LocalTime</span> <span class="nf">convert</span><span class="o">(</span><span class="nc">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">LocalTime</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">s</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Add a <code class="language-plaintext highlighter-rouge">MongoConfiguration</code> class in the package <code class="language-plaintext highlighter-rouge">com.okta.developer.records.configuration</code>, to register the converters:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.configuration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.repository.LocalDateConverter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.repository.LocalTimeConverter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Bean</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Configuration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.mongodb.core.convert.MongoCustomConversions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MongoConfiguration</span> <span class="o">{</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">MongoCustomConversions</span> <span class="nf">mongoCustomConversions</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">MongoCustomConversions</span><span class="o">(</span>
<span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span>
<span class="k">new</span> <span class="nf">LocalDateConverter</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">LocalTimeConverter</span><span class="o">()));</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Add a <code class="language-plaintext highlighter-rouge">StatsService</code> interface in the <code class="language-plaintext highlighter-rouge">com.okta.developer.records.service</code> package:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.service</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.EndOfGame</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.MentalStateDamage</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">StatsService</span> <span class="o">{</span>
<span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">MentalStateDamage</span><span class="o">&gt;</span> <span class="nf">queryMentalStateAverageDamage</span><span class="o">();</span>
<span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">EndOfGame</span><span class="o">&gt;</span> <span class="nf">getAll</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Add a <code class="language-plaintext highlighter-rouge">DefaultStatsService</code> class for the implementation in the <code class="language-plaintext highlighter-rouge">com.okta.developer.records.service</code> package:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.service</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.EndOfGame</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.MentalStateDamage</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.repository.StatsRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Service</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>
<span class="nd">@Service</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">DefaultStatsService</span> <span class="kd">implements</span> <span class="nc">StatsService</span> <span class="o">{</span>
<span class="nd">@Autowired</span>
<span class="kd">private</span> <span class="nc">StatsRepository</span> <span class="n">statsRepository</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">MentalStateDamage</span><span class="o">&gt;</span> <span class="nf">queryMentalStateAverageDamage</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">statsRepository</span><span class="o">.</span><span class="na">queryMentalStateAverageDamage</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">EndOfGame</span><span class="o">&gt;</span> <span class="nf">getAll</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">statsRepository</span><span class="o">.</span><span class="na">findAll</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Add a <code class="language-plaintext highlighter-rouge">StatsController</code> class in the <code class="language-plaintext highlighter-rouge">com.okta.developer.records.controller</code> package:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.controller</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.EndOfGame</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.MentalStateDamage</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.service.StatsService</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.GetMapping</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>
<span class="nd">@RestController</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">StatsController</span> <span class="o">{</span>
<span class="nd">@Autowired</span>
<span class="kd">private</span> <span class="nc">StatsService</span> <span class="n">statsService</span><span class="o">;</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/endOfGame"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">EndOfGame</span><span class="o">&gt;</span> <span class="nf">getAllEndOfGame</span><span class="o">(){</span>
<span class="k">return</span> <span class="n">statsService</span><span class="o">.</span><span class="na">getAll</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/mentalStateAverageDamage"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">MentalStateDamage</span><span class="o">&gt;</span> <span class="nf">getMentalStateAverageDamage</span><span class="o">(){</span>
<span class="k">return</span> <span class="n">statsService</span><span class="o">.</span><span class="na">queryMentalStateAverageDamage</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The controller enables the <code class="language-plaintext highlighter-rouge">/endOfGame</code> endpoint to get all entries, and the <code class="language-plaintext highlighter-rouge">/mentalStateAverageDamage</code> endpoint, that returns the damage in each category, as in average by mental state.</p>
<h3 id="test-java-records-in-the-web-layer">Test Java records in the web layer</h3>
<p>Create a <code class="language-plaintext highlighter-rouge">StatsControllerTest</code> class in the package <code class="language-plaintext highlighter-rouge">com.okta.developer.records.controller</code> under the <code class="language-plaintext highlighter-rouge">src/test</code> folder, to verify the endpoints’ basic functionality with a web test. In this test, only the web slice is verified:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.okta.developer.records.controller</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.EndOfGame</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.domain.MentalStateDamage</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.okta.developer.records.service.StatsService</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.Test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.Logger</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.LoggerFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.boot.test.mock.mockito.MockBean</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.test.web.reactive.server.WebTestClient</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalDate</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalTime</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">mockito</span><span class="o">.</span><span class="na">BDDMockito</span><span class="o">.</span><span class="na">given</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">security</span><span class="o">.</span><span class="na">test</span><span class="o">.</span><span class="na">web</span><span class="o">.</span><span class="na">reactive</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">SecurityMockServerConfigurers</span><span class="o">.</span><span class="na">mockOidcLogin</span><span class="o">;</span>
<span class="nd">@WebFluxTest</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">StatsControllerTest</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="nc">Logger</span> <span class="n">logger</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="nc">StatsControllerTest</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="nd">@MockBean</span>
<span class="kd">private</span> <span class="nc">StatsService</span> <span class="n">statsService</span><span class="o">;</span>
<span class="nd">@Autowired</span>
<span class="kd">private</span> <span class="nc">WebTestClient</span> <span class="n">webTestClient</span><span class="o">;</span>
<span class="nd">@Test</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">testGet_noAuth_returnsNotAuthorized</span><span class="o">(){</span>
<span class="n">webTestClient</span>
<span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">uri</span><span class="o">(</span><span class="s">"/endofgame"</span><span class="o">)</span>
<span class="o">.</span><span class="na">exchange</span><span class="o">()</span>
<span class="o">.</span><span class="na">expectStatus</span><span class="o">().</span><span class="na">is3xxRedirection</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Test</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">testGet_withOidcLogin_returnsOk</span><span class="o">(){</span>
<span class="nc">EndOfGame</span> <span class="n">endOfGame</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">EndOfGame</span><span class="o">(</span><span class="s">"1"</span><span class="o">,</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">now</span><span class="o">(),</span> <span class="nc">LocalTime</span><span class="o">.</span><span class="na">now</span><span class="o">(),</span> <span class="s">"happy"</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="mi">1</span><span class="o">);</span>
<span class="n">given</span><span class="o">(</span><span class="n">statsService</span><span class="o">.</span><span class="na">getAll</span><span class="o">()).</span><span class="na">willReturn</span><span class="o">(</span><span class="nc">Flux</span><span class="o">.</span><span class="na">just</span><span class="o">(</span><span class="n">endOfGame</span><span class="o">));</span>
<span class="n">webTestClient</span><span class="o">.</span><span class="na">mutateWith</span><span class="o">(</span><span class="n">mockOidcLogin</span><span class="o">())</span>
<span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">uri</span><span class="o">(</span><span class="s">"/endOfGame"</span><span class="o">)</span>
<span class="o">.</span><span class="na">exchange</span><span class="o">()</span>
<span class="o">.</span><span class="na">expectStatus</span><span class="o">().</span><span class="na">is2xxSuccessful</span><span class="o">()</span>
<span class="o">.</span><span class="na">expectBody</span><span class="o">()</span>
<span class="o">.</span><span class="na">jsonPath</span><span class="o">(</span><span class="s">"$.length()"</span><span class="o">).</span><span class="na">isNumber</span><span class="o">()</span>
<span class="o">.</span><span class="na">jsonPath</span><span class="o">(</span><span class="s">"$.length()"</span><span class="o">).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"1"</span><span class="o">)</span>
<span class="o">.</span><span class="na">jsonPath</span><span class="o">(</span><span class="s">"$[0].mentalState"</span><span class="o">).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"happy"</span><span class="o">)</span>
<span class="o">.</span><span class="na">jsonPath</span><span class="o">(</span><span class="s">"$[0].damageTaken"</span><span class="o">).</span><span class="na">isNumber</span><span class="o">()</span>
<span class="o">.</span><span class="na">jsonPath</span><span class="o">(</span><span class="s">"$[0].damageToPlayers"</span><span class="o">).</span><span class="na">isNumber</span><span class="o">()</span>
<span class="o">.</span><span class="na">jsonPath</span><span class="o">(</span><span class="s">"$[0].damageToStructures"</span><span class="o">).</span><span class="na">isNumber</span><span class="o">()</span>
<span class="o">.</span><span class="na">jsonPath</span><span class="o">(</span><span class="s">"$[0].date"</span><span class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisNotEmpty&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtjsonPath&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot$[0].timeOfDay&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisNotEmpty&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtconsumeWith&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresponse&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt-&amp;ampgt;&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtinfo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresponse&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gttoString&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()));&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@Test&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtpublic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkt&amp;quot&amp;gtvoid&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnf&amp;quot&amp;gttestGetMentalStateAverageDamage_withOidcLogin_returnsOk&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(){&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMentalStateDamage&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtmentalStateDamage&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotk&amp;quot&amp;gtnew&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMentalStateDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quothappy&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotmf&amp;quot&amp;gt0.0&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotmf&amp;quot&amp;gt0.0&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotmf&amp;quot&amp;gt0.0&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtgiven&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtstatsService&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtqueryMentalStateAverageDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtwillReturn&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtFlux&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtjust&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtmentalStateDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt));&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtwebTestClient&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtmutateWith&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtmockOidcLogin&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt())&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtget&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt().&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gturi&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot/mentalStateAverageDamage&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt)&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtexchange&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtexpectStatus&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt().&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtis2xxSuccessful&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtexpectBody&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtjsonPath&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot$.length()&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisEqualTo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot1&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt)&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtjsonPath&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot$.[0].mentalState&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisEqualTo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quothappy&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt)&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtconsumeWith&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresponse&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt-&amp;ampgt;&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtinfo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresponse&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gttoString&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()));&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtRun the tests with the following Maven command:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-shell highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt./mvnw &amp;ltspan class=&amp;quotnb&amp;quot&amp;gttest&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt-Dtest&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gtStatsControllerTest
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtThe &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gt/mentalStateAverageDamage&amp;lt/code&amp;gt test above also verifies that the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtMentalStateDamage&amp;lt/code&amp;gt record type is correctly handled when used as a response body. For the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gt/endOfGame&amp;lt/code&amp;gt you should see response logs similar to this:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-json highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt[&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt{&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotid&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quot1&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdate&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quot2021-10-21&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quottimeOfDay&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quot12:02:34.233944363&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotmentalState&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quothappy&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageTaken&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt1&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageToPlayers&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt1&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageToStructures&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt1&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt}&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt]&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;lth3 id=&amp;quottest-java-records-in-the-database-layer&amp;quot&amp;gtTest Java records in the database layer&amp;lt/h3&amp;gt
&amp;ltp&amp;gtDownload the test dataset from &amp;lta href=&amp;quothttps://github.com/oktadev/okta-java-records-example/blob/main/src/test/resources/stats.json&amp;quot&amp;gtGitHub&amp;lt/a&amp;gt with HTTPie, and copy it to &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtsrc/test/resources/stats.json&amp;lt/code&amp;gt:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-shell highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gthttps &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt-d&amp;lt/span&amp;gt raw.githubusercontent.com/oktadev/okta-java-records-example/9a60f81349cdffebbe001719256b0883493f987d/src/test/resources/stats.json
&amp;ltspan class=&amp;quotnb&amp;quot&amp;gtmkdir &amp;lt/span&amp;gtsrc/test/resources
&amp;ltspan class=&amp;quotnb&amp;quot&amp;gtmv &amp;lt/span&amp;gtstats.json src/test/resources/.
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtCreate the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtStatsRepositoryTest&amp;lt/code&amp;gt class in the package &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtcom.okta.developer.records.repository&amp;lt/code&amp;gt under the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtsrc/test&amp;lt/code&amp;gt folder, to verify the database slice:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-java highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtpackage&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtcom.okta.developer.records.repository&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtcom.okta.developer.records.configuration.MongoConfiguration&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtcom.okta.developer.records.domain.EndOfGame&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtcom.okta.developer.records.domain.MentalStateDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.junit.jupiter.api.AfterAll&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.junit.jupiter.api.BeforeAll&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.junit.jupiter.api.Test&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.slf4j.Logger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.slf4j.LoggerFactory&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.springframework.beans.factory.annotation.Autowired&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.springframework.context.annotation.Import&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.testcontainers.containers.Container&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.testcontainers.containers.MongoDBContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.testcontainers.utility.DockerImageName&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtorg.testcontainers.utility.MountableFile&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtreactor.core.publisher.Flux&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtjava.io.IOException&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtjava.util.List&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkn&amp;quot&amp;gtimport&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnn&amp;quot&amp;gtstatic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtorg&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtassertj&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtcore&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtapi&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtAssertions&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtassertThat&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@DataMongoTest&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtexcludeAutoConfiguration&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtEmbeddedMongoAutoConfiguration&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtclass&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt)&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@Import&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMongoConfiguration&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtclass&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt)&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtpublic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtclass&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtStatsRepositoryTest&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt{&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtprivate&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtstatic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtfinal&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtLogger&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtLoggerFactory&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtgetLogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtStatsRepositoryTest&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtclass&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@Autowired&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtprivate&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtStatsRepository&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtstatsRepository&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt;&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtprivate&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtstatic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtfinal&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMongoDBContainer&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtmongoDBContainer&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotk&amp;quot&amp;gtnew&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMongoDBContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtDockerImageName&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtparse&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotmongo:bionic&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt))&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtwithExposedPorts&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt27017&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt)&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtwithCopyFileToContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMountableFile&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtforClasspathResource&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotstats.json&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt),&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot/stats.json&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt)&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtwithEnv&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotMONGO_INIT_DATABASE&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotfortnite&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@BeforeAll&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtpublic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtstatic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkt&amp;quot&amp;gtvoid&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnf&amp;quot&amp;gtsetUp&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtthrows&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtIOException&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnc&amp;quot&amp;gtInterruptedException&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt{&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtmongoDBContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtsetPortBindings&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtList&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtof&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot27017:27017&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt));&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtmongoDBContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtstart&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtExecResult&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtmongoDBContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtexecInContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotmongoimport&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot--verbose&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot--db=fortnite&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot--collection=stats&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot--file=/stats.json&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quot--jsonArray&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtinfo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtgetStdout&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt());&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtinfo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtgetStderr&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt());&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtinfo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotexit code={}&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtgetExitCode&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt());&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@Test&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtpublic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkt&amp;quot&amp;gtvoid&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnf&amp;quot&amp;gttestGetAll&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(){&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtFlux&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;amplt;&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtEndOfGame&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;ampgt;&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtstats&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtstatsRepository&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtfindAll&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtList&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;amplt;&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtEndOfGame&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;ampgt;&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtstats&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtcollectList&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt().&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtblock&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtassertThat&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisNotEmpty&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtassertThat&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtsize&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt().&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisEqualTo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt87&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@Test&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtpublic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkt&amp;quot&amp;gtvoid&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnf&amp;quot&amp;gttestQueryMentalStateAverageDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(){&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtFlux&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;amplt;&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMentalStateDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;ampgt;&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtstats&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtstatsRepository&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtqueryMentalStateAverageDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtList&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;amplt;&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnc&amp;quot&amp;gtMentalStateDamage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt&amp;ampgt;&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt &amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotn&amp;quot&amp;gtstats&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtcollectList&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt().&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtblock&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtassertThat&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisNotEmpty&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtassertThat&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtsize&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt().&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisEqualTo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt2&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtassertThat&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtget&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt0&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtmentalState&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisIn&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotsober&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quothigh&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtassertThat&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtget&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt1&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtmentalState&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt()).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtisIn&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quotsober&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt,&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt&amp;quothigh&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt);&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtinfo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtget&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt0&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gttoString&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt());&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtlogger&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtinfo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotn&amp;quot&amp;gtresult&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtget&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmi&amp;quot&amp;gt1&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt).&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gttoString&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt());&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotnd&amp;quot&amp;gt@AfterAll&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotkd&amp;quot&amp;gtpublic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkd&amp;quot&amp;gtstatic&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotkt&amp;quot&amp;gtvoid&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnf&amp;quot&amp;gttearDown&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt(){&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotn&amp;quot&amp;gtmongoDBContainer&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt.&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtstop&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt();&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quoto&amp;quot&amp;gt}&amp;lt/span&amp;gt
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gt&amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gt@DataMongoTest&amp;lt/code&amp;gt configures the data layer for testing. For this test, I evaluated using &amp;lta href=&amp;quothttps://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#core.repository-populators&amp;quot&amp;gtrepository populator&amp;lt/a&amp;gt, but decided to go for a Testcontainers MongoDB instance instead, to reuse the import process and &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtdata.json&amp;lt/code&amp;gt file later with Docker Compose. Using a container MongoDB instance requires disabling the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtEmbeddedMongoAutoConfiguration&amp;lt/code&amp;gt.&amp;lt/p&amp;gt
&amp;ltp&amp;gtIn the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtsetUp()&amp;lt/code&amp;gt above, the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtmongoimport&amp;lt/code&amp;gt tool is executed in the test container, initializing the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtstats&amp;lt/code&amp;gt collection with the sample dataset.&amp;lt/p&amp;gt
&amp;ltp&amp;gtRun the tests with the following Maven command:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-shell highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt./mvnw &amp;ltspan class=&amp;quotnb&amp;quot&amp;gttest&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt-Dtest&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gtStatsRepositoryTest
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtIf the import runs successfully, the following line should appear in the test logs:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-text highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt87 document(s) imported successfully. 0 document(s) failed to import.
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtAlso, you can inspect the response in the logs for the average damage test:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gtMentalStateDamage[mentalState=sober, damageToPlayers=604.3777777777777, damageToStructures=3373.511111111111, damageTaken=246.46666666666667]
MentalStateDamage[mentalState=high, damageToPlayers=557.547619047619, damageToStructures=2953.8571428571427, damageTaken=241.71428571428572]
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtFor this single-player dataset, the damage taken or inflicted was not orders of magnitude different when sober than when high.&amp;lt/p&amp;gt
&amp;lth3 id=&amp;quotrun-the-application&amp;quot&amp;gtRun the application&amp;lt/h3&amp;gt
&amp;ltp&amp;gtCreate a &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtdocker&amp;lt/code&amp;gt folder in the root of the project, and add the following &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtdocker-compose.yml&amp;lt/code&amp;gt file there:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-yaml highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt&amp;ltspan class=&amp;quotna&amp;quot&amp;gtversion&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt3.1&amp;quot&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtservices&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtmongodb&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtimage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gtmongo:bionic&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtenvironment&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt-&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gtMONGO_INIT_DATABASE=fortnite&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtports&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt-&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt27017:27017&amp;quot&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtvolumes&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt-&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt../src/test/resources/stats.json:/seed/stats.json&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt-&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gt./initdb.sh:/docker-entrypoint-initdb.d/initdb.sh&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtdemo&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtimage&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gtrecords-demo:0.0.1-SNAPSHOT&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtports&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt-&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots&amp;quot&amp;gt8080:8080&amp;quot&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtenvironment&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt-&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gtSPRING_DATA_MONGODB_HOST=mongodb&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotna&amp;quot&amp;gtdepends_on&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt:&amp;lt/span&amp;gt
&amp;ltspan class=&amp;quotpi&amp;quot&amp;gt-&amp;lt/span&amp;gt &amp;ltspan class=&amp;quots&amp;quot&amp;gtmongodb&amp;lt/span&amp;gt
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtAdd also the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtinitdb.sh&amp;lt/code&amp;gt script in the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtdocker&amp;lt/code&amp;gt folder, to import the test data into MongoDB, with the following content:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-shell highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gtmongoimport &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt--verbose&amp;lt/span&amp;gt &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt--db&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gtfortnite &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt--collection&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gtstats &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt--file&amp;lt/span&amp;gt&amp;ltspan class=&amp;quoto&amp;quot&amp;gt=&amp;lt/span&amp;gt/seed/stats.json &amp;ltspan class=&amp;quotnt&amp;quot&amp;gt--jsonArray&amp;lt/span&amp;gt
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtIn the project root, generate the application container image with the following Maven command:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-shell highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt./mvnw spring-boot:build-image
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtRun the application with Docker Compose:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-shell highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt&amp;ltspan class=&amp;quotnb&amp;quot&amp;gtcd &amp;lt/span&amp;gtdocker
docker-compose up
&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;ltp&amp;gtOnce the services are up, go to &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gthttp://localhost:8080/mentalStateAverageDamage&amp;lt/code&amp;gt, and you should see the Okta login page:&amp;lt/p&amp;gt
&amp;ltp&amp;gt&amp;ltimg src=&amp;quot/assets-jekyll/blog/java-records/okta-login-0cf34a18bc379738cfc6abf6e2f91b0bd9d3498d5aa01af1cf95dc8b8c0374c0.png&amp;quot alt=&amp;quotOkta Login Form&amp;quot width=&amp;quot400&amp;quot class=&amp;quotcenter-image&amp;quot /&amp;gt&amp;lt/p&amp;gt
&amp;ltp&amp;gtSign in with your Okta credentials, and if successful, it will redirect to the &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gt/mentalStateAverageDamage&amp;lt/code&amp;gt endpoint, and you should see a response body like the following:&amp;lt/p&amp;gt
&amp;ltdiv class=&amp;quotlanguage-json highlighter-rouge&amp;quot&amp;gt&amp;ltdiv class=&amp;quothighlight&amp;quot&amp;gt&amp;ltpre class=&amp;quothighlight&amp;quot&amp;gt&amp;ltcode&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt[&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt{&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotmentalState&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quotsober&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageToPlayers&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmf&amp;quot&amp;gt604.3777777777777&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageToStructures&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmf&amp;quot&amp;gt3373.511111111111&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageTaken&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmf&amp;quot&amp;gt246.46666666666667&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt},&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt{&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotmentalState&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quots2&amp;quot&amp;gt&amp;quothigh&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageToPlayers&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmf&amp;quot&amp;gt557.547619047619&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageToStructures&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmf&amp;quot&amp;gt2953.8571428571427&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt,&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotnl&amp;quot&amp;gt&amp;quotdamageTaken&amp;quot&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt:&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotmf&amp;quot&amp;gt241.71428571428572&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt}&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotp&amp;quot&amp;gt]&amp;lt/span&amp;gt&amp;ltspan class=&amp;quotw&amp;quot&amp;gt
&amp;lt/span&amp;gt&amp;lt/code&amp;gt&amp;lt/pre&amp;gt&amp;lt/div&amp;gt&amp;lt/div&amp;gt
&amp;lth2 id=&amp;quotjava-records-advantages-and-limitations&amp;quot&amp;gtJava records advantages and limitations&amp;lt/h2&amp;gt
&amp;ltp&amp;gtWhile Java record is more concise for declaring data carrier classes, the &amp;ldquowar on boilerplate&amp;rdquo is not a goal of this construct, Records are not meant to add features like properties or annotation-driven code generation, as &amp;lta href=&amp;quothttps://projectlombok.org/&amp;quot&amp;gtProject Lombok&amp;lt/a&amp;gt does. Record semantics provide benefits for modeling an immutable state data type. No hidden state is allowed, as no instance fields can be defined outside the header, hence the transparent claim. Compiler generated &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtequals()&amp;lt/code&amp;gt and &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gthashCode()&amp;lt/code&amp;gt avoid error-prone coding. Serialization and deserialization into JSON are straightforward thanks to its canonical constructor. Summarizing some of the Java Record features discussed in this post:&amp;lt/p&amp;gt
&amp;ltp&amp;gtAdvantages&amp;lt/p&amp;gt
&amp;ltul&amp;gt
&amp;ltli&amp;gtConcise syntax&amp;lt/li&amp;gt
&amp;ltli&amp;gtImmutable state&amp;lt/li&amp;gt
&amp;ltli&amp;gtCompiler generated &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gtequals()&amp;lt/code&amp;gt and &amp;ltcode class=&amp;quotlanguage-plaintext highlighter-rouge&amp;quot&amp;gthashCode()&amp;lt/code&amp;gt&amp;lt/li&amp;gt
&amp;ltli&amp;gtStraightforward JSON serialization and deserialization&amp;lt/li&amp;gt
&amp;lt/ul&amp;gt
&amp;ltp&amp;gtLimitations&amp;lt/p&amp;gt
&amp;ltul&amp;gt
&amp;ltli&amp;gtImmutable state&amp;lt/li&amp;gt
&amp;ltli&amp;gtCannot be used as JPA/Hibernate entities&amp;lt/li&amp;gt
&amp;ltli&amp;gtCannot be extended or inherit a class&amp;lt/li&amp;gt
&amp;lt/ul&amp;gt
&amp;lth2 id=&amp;quotlearn-more-about-java-and-spring&amp;quot&amp;gtLearn more about Java and Spring&amp;lt/h2&amp;gt
&amp;ltp&amp;gtI hope you enjoyed this tutorial and learned about Java record semantics, its benefits, and limitations. Before choosing this feature, make sure to find out if your favorite frameworks support it. Fortunately, Spring Boot support for Java Records was recently added in 2.5.x releases through Jackson 2.12.x. I was not able to find comments about records in Spring Data documentation.&amp;lt/p&amp;gt
&amp;ltp&amp;gtTo continue learning about Java records, Okta security, and Spring WebFlux, check out the links below:&amp;lt/p&amp;gt
&amp;ltul&amp;gt
&amp;ltli&amp;gt&amp;lta href=&amp;quothttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Record.html&amp;quot&amp;gtjava.lang.Record&amp;lt/a&amp;gt&amp;lt/li&amp;gt
&amp;ltli&amp;gt&amp;lta href=&amp;quothttps://developer.okta.com/blog/2021/01/20/reactive-java-microservices&amp;quot&amp;gtReactive Java Microservices with Spring Boot and JHipster&amp;lt/a&amp;gt&amp;lt/li&amp;gt
&amp;ltli&amp;gt&amp;lta href=&amp;quothttps://developer.okta.com/blog/2021/05/12/spring-boot-r2dbc&amp;quot&amp;gtR2DBC and Spring for Non-Blocking Database Access&amp;lt/a&amp;gt&amp;lt/li&amp;gt
&amp;ltli&amp;gt&amp;lta href=&amp;quothttps://developer.okta.com/blog/2020/04/15/spring-cloud-stream&amp;quot&amp;gtA Quick Guide to Spring Cloud Stream&amp;lt/a&amp;gt&amp;lt/li&amp;gt
&amp;ltli&amp;gt&amp;lta href=&amp;quothttps://developer.okta.com/blog/2021/05/05/client-credentials-spring-security&amp;quot&amp;gtHow to Use Client Credentials Flow with Spring Security&amp;lt/a&amp;gt&amp;lt/li&amp;gt
&amp;ltli&amp;gt&amp;lta href=&amp;quothttps://developer.okta.com/blog/2021/05/19/spring-security-testing&amp;quot&amp;gtBetter Testing with Spring Security Test&amp;lt/a&amp;gt&amp;lt/li&amp;gt
&amp;lt/ul&amp;gt
&amp;ltp&amp;gtYou can find the completed code for this tutorial on GitHub in the &amp;lta href=&amp;quothttps://github.com/oktadev/okta-java-records-example.git&amp;quot&amp;gtoktadev/okta-java-records-example&amp;lt/a&amp;gt repository.&amp;lt/p&amp;gt
&amp;ltp&amp;gtIf you liked this tutorial, chances are you will like others we publish. Please follow &amp;lta href=&amp;quothttps://twitter.com/oktadev&amp;quot&amp;gt@oktadev on Twitter&amp;lt/a&amp;gt and &amp;lta href=&amp;quothttps://youtube.com/oktadev&amp;quot&amp;gtsubscribe to our YouTube channel&amp;lt/a&amp;gt to get notified when we publish new developer tutorials.&amp;lt/p&amp;gt

Java Records: A WebFlux and Spring Data Example