Use Vavr List and Option with Hibernate 6
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