Using the vavr-hibernate6 library, you can use Vavr functional data types such as List and Option with Hibernate 6. This allows for cleaner code and null safety (no more using Java null). The goal is to have entity model code looking like this:

import io.vavr.collection.List;
import io.vavr.control.Option;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@Table(name = "customers")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Option<String> name;

    private Option<Integer> number;

    private Option<String> city;

    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
    private List<Order> orders;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "membership_id")
    private Option<Membership> membership;
}

1) Add dependency

To achieve this, we need to override some Spring data functions, using the vavr-hibernate6 library. First, add this dependecy to your pom.xml:

<dependency>
    <groupId>io.github.jleblanc64</groupId>
    <artifactId>vavr-hibernate6</artifactId>
    <version>1.0.1</version>
</dependency>

2) Load custom Hibernate code

Load custom code before Spring boot beans are initialized. Add class ConfigInit:

import io.github.jleblanc64.hibernate6.hibernate.VavrHibernate6;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class ConfigInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext ctx) {
        VavrHibernate6.override();
    }
}

and register the class in the file: src/main/resources/META-INF/spring.factories (replace com.demo.spring with your own package) :

org.springframework.context.ApplicationContextInitializer=com.demo.spring.ConfigInit

If you are not using Spring Boot, you can load the VavrHibernate6.override() code anywhere that will be executed before the Hibernate entities are loaded.

3) Override Jackson serialization/deserialization

To use Vavr in DTOs, we also need to add custom Jackson converters. To do this, just add the class WebMvcConfig:

import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.jleblanc64.hibernate6.jackson.deser.UpdateOM;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    ObjectMapper om;

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        UpdateOM.update(om, converters);
    }
}

Vavr DTO to Hibernate entity code example

You can now enjoy concise functional code in your app. Like for instance using List.map() without the whole .stream() and .collect() boilerplate code. For instance, when mapping a DTO to an Entity:

import com.demo.model.Customer;
import io.vavr.collection.List;
import io.vavr.control.Option;
import lombok.*;

@Getter
@Setter
@NoArgsConstructor
public class CustomerDtoReq {
    private Option<String> name;
    private Option<Integer> number;
    private Option<String> city;
    private List<OrderDto> orders;
    private Option<MembershipDto> membership;
    
        public Customer toEntity() {
            var c = new Customer();
            c.setName(name);
            c.setNumber(number);
            c.setCity(city);
            c.setOrders(orders.map(x -> x.toEntity(c)));
            c.setMembership(membership.map(MembershipDto::toEntity));
    
            return c;
        }
}

Full Vavr + Spring boot 3 + Hibernate 6 demo

You can clone the following Github repo and run the class: src/test/java/com/demo/ApplicationTests.java

Integrate Vavr with Hibernate 5.x

For Hibernate version 5.x, you can use:

import io.github.jleblanc64.hibernate5.hibernate.VavrHibernate5;

VavrHibernate5.override();

You can also find a full demo for Spring boot 2 + Hibernate 5 here

Questions

If you have a question, please open an issue in my Github repo issues


<
Previous Post
Override any Java method at runtime
>
Next Post
Use Vavr List and Option with Spring 6 DTOs