Using the LibCustom 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;
}

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

<dependency>
    <groupId>io.github.jleblanc64</groupId>
    <artifactId>lib-custom</artifactId>
    <version>1.0.7</version>
</dependency>

A good place to load the custom code is in the Spring config function:

import io.github.jleblanc64.libcustom.LibCustom;
import io.github.jleblanc64.libcustom.custom.hibernate.VavrHibernate6;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;

@Bean
public DataSource getDataSource() {
    VavrHibernate6.override();
    LibCustom.load();
    (...)

Indeed this code is executed before the persistence layer instantiation. You can find the whole DataSourceConfig code in my Github repo. If you are not using Spring Boot, you can load the LibCustom code anywhere that will be executed before the Hibernate entities are loaded. A good place generally is as a static code snippet in a config file of your application.

Thanks to this logic, you can 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;
        }
}

Integrate Vavr with Hibernate 5.x

For Hibernate version 5.x, you can use:

import io.github.jleblanc64.libcustom.custom.hibernate.VavrHibernate5;

VavrHibernate5.override();

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