Using Java Records in Spring Boot Appications

In this blog we will see how to use Java reocrds in Spring Boot applications.

What are Java Records?

As explained in one of my previous blog post, Java Records are a new feature introduced in JDK 14 as a preview and later finalized in JDK 16. They provide a clean and concise way to define immutable data classes-classes that primarily serve as containers for data and have little or no behavior. With Java Records, you can reduce boilerplate code associated with declaring fields, constructors, accessors, and methods like equals() and hashCode().

Using Java Records in Spring Boot

Type-safe Configuration Properties

Type-safe Configuration Properties is a feature in Spring Boot that allows you to bind external properties with Java objects in a type-safe way.It provides infrastructure to bind @ConfigurationProperties types and register them as beans.

If you have following properties

app.property-one=value_one
app.property-two=123Code language: Java (java)

You can define a class like and bind these properties

@ConfigurationProperties(prefix="app")
public class AppProperties2 {
    private String propertyOne;
    private int propertyTwo;


    public String getPropertyOne() {
        return propertyOne;
    }

    public void setPropertyOne(String propertyOne) {
        this.propertyOne = propertyOne;
    }

    public int getPropertyTwo() {
        return propertyTwo;
    }

    public void setPropertyTwo(int propertyTwo) {
        this.propertyTwo = propertyTwo;
    }
}
Code language: Java (java)

With Java reocrd feature, you can simply replace the class by Java record.

@ConfigurationProperties(prefix="app")
public record AppProperties (String propertyOne,int propertyTwo) {
   
}Code language: Java (java)

You can also add validation to properties with compact constructor.

@ConfigurationProperties(prefix="app")
public record AppProperties (String propertyOne,int propertyTwo) {
    public AppProperties{
        if(!StringUtils.hasLength(propertyOne) || propertyTwo <= 0) {
            throw new IllegalArgumentException("Data is invalid");
        }
    }
}Code language: Java (java)

If you are using spring boot validation framework, you can add validation directly without adding compact constructor.

@Validated
@ConfigurationProperties(prefix="app")
public record AppProperties (@NotNull String propertyOne,@Positive int propertyTwo) {
}Code language: Java (java)

Java DTO Classes.

You can replace Java POJO DTO classes in Spring Boot application with record

public record EmployeeDto(Integer id, String first_name,
                          String last_name, Gender gender,
                          LocalDate birth_date, LocalDate hire_date,
                          DepartmentDto department) implements Serializable {
}
Code language: Java (java)

you can also add the validations

public record EmployeeDto(Integer id, @NotBlank(message = "FirstName should not be blank") String first_name,
                          @NotBlank String last_name, Gender gender,
                          @PastOrPresent LocalDate birth_date, LocalDate hire_date,
                          DepartmentDto department) implements Serializable {
}Code language: Java (java)

Use them in Spring Boot controller classes instead of POJO classes.

package dev.fullstackcode.eis.controller;

@RestController
@RequestMapping("/employee")
@Validated
public class EmployeeController {


    private final EmployeeService employeeService;
    private final AppProperties appProperties;

    @Autowired
    public EmployeeController(EmployeeService employeeService, AppProperties appProperties) {
        this.employeeService = employeeService;
        this.appProperties = appProperties;
    }


    @GetMapping("/properties")
    public String displayProperties() {
        return "Property One: " + appProperties.propertyOne()
                + ", Property Two: " + appProperties.propertyTwo();
    }

    @GetMapping("/{id}")
    public EmployeeDto getEmployee(@PathVariable @Positive Integer id) {

        Employee employeeEntity = employeeService.getEmployeeById(id);
        return mapEntityToDto(employeeEntity);
    }

    @ResponseStatus(HttpStatus.CREATED) // send HTTP 201 instead of 200 as new object    // created
    @PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE})
    public EmployeeDto createEmployee(@RequestBody @Valid EmployeeDto employeeDto) {
        Employee empEntity = mapDtoToEntity(employeeDto);
        Employee employeeEntity = employeeService.createEmployee(empEntity);
        return mapEntityToDto(employeeEntity);

    }

    private Employee mapDtoToEntity(EmployeeDto employeeDto) {
        Employee employeeEntity = new Employee();
        employeeEntity.setId(employeeDto.id());
        employeeEntity.setGender(employeeDto.gender());
        employeeEntity.setFirst_name(employeeDto.first_name());
        employeeEntity.setLast_name(employeeDto.last_name());
        employeeEntity.setHire_date(employeeDto.hire_date());
        employeeEntity.setBirth_date(employeeDto.birth_date());
        if (employeeDto.department() != null) {
            Department dept = new Department();
            dept.setName(employeeDto.department().dept_name());
            dept.setId(employeeDto.department().id());
            employeeEntity.setDepartment(dept);
        }

        return employeeEntity;
    }

    private EmployeeDto mapEntityToDto(Employee employee) {

        return new EmployeeDto(employee.getId(), employee.getFirst_name(), employee.getLast_name(), employee.getGender(), employee.getBirth_date(), employee.getHire_date(),
                employee.getDepartment() != null ? new DepartmentDto(employee.getDepartment().getId(), employee.getDepartment().getName()) : null);
    }

}

Code language: Java (java)

You can download complete source code for the blog post from GitHub

Similar Posts