Hibernate Reactive One to Many - Join Fetch Using Mutiny and Criteria Builder
Hi Everyone,
if you have landed here, I believe that you are stuck with the use case where you need to eager fetch some items and you are using hibernate reactive .
While eager fetch is only good for some specific use cases, try avoiding it as much as you can. Use eager fetch where you are not going to load tons of data since it can bring entire operations slow in de/serialization process.
Just a tip: if your data that you are doing to fetch does not change all the time, use caching. You can have application level caching setup if you do not want to setup redis or other caching services. (Don't forget to write invalidate api to clear the cache )
So, lets begin with our use case :
Suppose we have two classes, named User and Roles
These entities are linked with each other in One to Many relationship.
Order => HasMany => OrderItems
Lets define these classes (I will be removing import statements for clarity). All imports are from javax.persistence and hibernate-reactive.
We will be using Kotlin.
Our Order class
@Entity
class Order {
@Id //other annotations go here
var id:Long = 0 ;
var orderDate:LocalDateTime = LocalDateTime.now();
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
var orderItems = mutableListOf<OrderItem>();
}
Now Lets look at our OrderItem class
@Entity
class OrderItem {
@Id
var id:Long = 0 ;
@ManyToOne(targetEntity = Order::class, fetch = FetchType.LAZY)
@JoinColumn(name="order_id")
lateinit var order:Order;
// other getter setters or use lombok
}
So, we have this use case now where we want to show the order and order items together. We are using plain entity classes but you can use your views and project your data as per need.
Lets configure our repository function (we are using repository pattern).
We will inject the mutiny sessionFactory available from hibernate-reactive.
@Inject
lateinit var sessionFactory:Mutiny.SessionFactory;
Here is our function definition
fun findAllOrdersWithItems():Uni<List<Order>>{
return sessionFactory.withSession { s->
val criteriaBuilder = sessionFactory.criteriaBuilder // (1)
val criteriaQuery: CriteriaQuery<Order> = criteriaBuilder.createQuery(Order::class.java) //(2)
val orderQuery: Root<Order> = criteriaQuery.from(Order::class.java) //(3)
criteriaQuery.select(orderQuery) //(4)
val entityGraph: EntityGraph<Order> = s.createEntityGraph(Order::class.java) //(5)
entityGraph.addAttributeNodes("orderItems") //(6)
val query = s.createQuery(criteriaQuery) //(7)
query.setPlan(entityGraph) // (8)
query.resultList //(9)
}
}
We have not applied any limit and pagination pipelines. You can add that as you want that to configure.
Lets look at what we did.
Using session factory we requested a criteria builder instance to create our criteria pipeline.
We created criteriaQuery on the parent class Order
We configured the root element of our criteria where results are going to be mapped.
We configured select operation.
In order to build the data we want to pick, we created an entity graph.
We added additional attributes to be loaded.( the lazy ones)
Using the session object s we finally created the query to be executed. The one created by using criteria builder.
Configured query plan( data we want to be part of graph)
Executed Query and invoked resultList that returns Uni<List<Order>>