Java Records in Spring Boot Rest API

Introduction to Records

Java 14 was released in March 2020 and one of the features that drew our attention was Records. Let’s start by explaining what a Record is and then proceed with the potential use cases.

Note: Java 17 is about to be released (September 2021) which is the first LTS version with records support.

From Openjdk JEP359:

Records provide a compact syntax for declaring classes which are transparent holders for shallowly immutable data.

This feature attempts to reduce the amount of boilerplate code in our applications as you only have to define the fields (equivalent to class fields). Records provide a public constructor (with all arguments), read methods for each field (equivalent to getters) and the implementation of hashCode, equals and toString methods.

This is how a Record and Class, for the same data structure, would look like.

Record


public record Person(String name, int age) {
}


Enter fullscreen mode Exit fullscreen mode

Class


public class Person {
  private String name;
  private int age;

  public Person(String name, int age) {
    ...
  }

  public boolean equals(Object o) {
    ...
  }

  public int hashCode() {
    ...
  }

  public String toString() {
    ...
  }
}


Enter fullscreen mode Exit fullscreen mode

As you will notice this is significantly less code given the class’ utility.

Usages

So where could the Records be used then? One potential application is in rest controllers. Sometimes it can be quite frustrating having to create a class only to be used as a return type or a request body in a rest controller.

In the next section we are going to demonstrate how to build a spring boot rest api with Records.

Demo

The code for this demo could be found here:

psideris89 / java-records-demo

Demo spring boot rest api for java 14 records demonstration

Step 1: Create Spring Boot app

You can use spring initialiser (https://start.spring.io/) to create a spring boot project. The configuration for our demo is:

  • Project: Gradle
  • Language: Java 16
  • Spring Boot: 2.5.4
  • Name: movies-demo
  • Dependencies: Spring Web

Everything else could be left with the default value.

Step 2: Project configuration

If you are using an IDE you need to use jdk 16. For Intellij set the project sdk to 16 and the project language level to 16.

Legacy configuration (java 14)

If you are using java 16 you can skip this section.

To use Records with jdk 14 you have to enable the preview features. You might be prompted to accept some terms if you want to use the preview features, so click accept and proceed.

In addition add the following configuration in build.gradle. There is similar configuration for maven.



compileJava {
    options.compilerArgs += ["--enable-preview"]
}

test {
    jvmArgs(['--enable-preview'])
}


Enter fullscreen mode Exit fullscreen mode

Step 3: Create Rest Controller

Before we create the rest controller we need to define the Records.

Director


public record Director(String name, String surname) {
}


Enter fullscreen mode Exit fullscreen mode

Movie


public record Movie(String id, Director director, Boolean released) {
}


Enter fullscreen mode Exit fullscreen mode

Next create a rest controller with 3 endpoints, one to retrieve all the movies, another to retrieve a movie by id and the last one to add a movie (we don’t actually save anything as this is to demonstrate deserialisation).



@RestController
public class MoviesController {

    private List<Movie> movies = List.of(
            new Movie("1", new Director("John", "Wick"), true),
            new Movie("2", new Director("Mary", "Poppins"), false),
            new Movie("3", new Director("Jack", "Sparrow"), true),
            new Movie("4", null, false));

    @GetMapping("/movies")
    public List<Movie> getMovies() {
        return movies;
    }

    @GetMapping("/movies/{id}")
    public Movie getMovie(@PathVariable String id) {
        return movies
                .stream()
                .filter(s -> s.id().equals(id))
                .findFirst()
                .orElseThrow(() -> new RuntimeException("Not found"));
    }

    @PostMapping("/movies")
    public Movie addMovie(@RequestBody Movie movie) {
        // Save the movie, be careful as List.of() is immutable
        return movie;
    }
}


Enter fullscreen mode Exit fullscreen mode

Step 4: Configure Jackson for Serialisation / Deserialisation

Start the application and call the first endpoint, localhost:8080/movies. The following error is produced as the objects cannot be serialised.

The solution to that is to use Jackson annotations and in specific JsonProperty annotation.



public record Director(@JsonProperty("name")String name, @JsonProperty("surname")String surname) {
}

public record Movie(@JsonProperty("id") String id, @JsonProperty("director") Director director, @JsonProperty("released") Boolean released) {
}


Enter fullscreen mode Exit fullscreen mode

Restart the application and you will be able to retrieve the list of movies.

The getMovie endpoint (localhost:8080/movies/1) uses the id() method which is provided by the Record. That way we can compare the id of each movie with the id provided in the api call.

Finally the addMovie endpoint is just a mock endpoint but it proves that deserialisation is also working. To test that you need to do a POST request to localhost:8080/movies with a valid body.



{
"id": "10",
"director": {
"name": "Jim",
"surname": "White"
},
"released": false
}

Enter fullscreen mode Exit fullscreen mode



Conclusion

What could Records mean for the future of java? Maybe they are not meant to be used that way, but in general that feature is something that a lot of java developers anticipated. The fact that Records are immutable is adding a massive value. All the above in combination with the reduction of the boilerplate code make Records a promising feature.

原文链接:Java Records in Spring Boot Rest API

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容