You Don’t Need Hibernate With Spring WebFlux and R2DBC
You Don’t Need Hibernate With Spring WebFlux and R2DBC
We focus on fetching composed entities from PostgreSQL and mapping them with Data Mapper pattern with a Spring ReactiveCrudRepository extension.
Join the DZone community and get the full member experience.Join For Free
One of issues when you work with relational databases in Java is that they are unable to translate object relationships (such as composition), due to the tabular nature of data sources. That means that as developers, we usually tend to have an intermediate layer, which is responsible to abstract the data source’s data organization. This is called ORM or object-relational mapping. In Spring's ecosystem the de facto standard is Hibernate, however it is not [yet] available for new non-blocking the Spring R2DBC API.
Frankly speaking, you don’t need Hibernate to do object mapping, as R2DBC is easy to use and you can do it yourself. And moreover it is a great pleasure, especially because we – Java devs – tend to rely on tools that are not state of the art of software design.
This post focuses on a problem of fetching composed entities from Postgresql (using INNER JOIN) and mapping it with Data Mapper pattern (as defined in P of EAA) with custom Spring ReactiveCrudRepository extension.
In the object-oriented programming, we work with objects that possess rich relationships between each other, based on aggregation, association, or composition. Imagine that you work on a task management app, where each task entity holds a reference to project entity. Or, another case – job app, where job entity points the corresponding employer object.
However, in relational databases, despite their name, data is organized in a tabular way. That means we need to have an intermediate layer between database and business logic. Such a mechanism is called object-relational mapping (or ORM), and it allows to access data entities in a way independent from how they are stored in data sources. In Java de facto choice is Hibernate, and as Spring developers we use it a lot with relational DBs, such as MySQL, Postgre, etc.
But, when you use non-blocking Webflux APIs and R2DBC to work with relational databases, you don’t have an ORM because Hibernate is not supported by R2DBC. Yet, maybe. And I think it is good – as Java developers we love to talk about software design, but we depend on tools, that are not best examples of software architectural principles. This post is about dealing with composition in entities without Hibernate in Spring R2DBC and PostgreSQL.
Consider the following example: we build a task management application, and we have
Project entities. Both are stored in separate tables, but are referenced using
project_id key. When we want to retrieve a list of tasks, we want to have information about corresponding projects, so we could show it in client apps. Yet, we can have two separate repos and then combine data it is highly inefficient idea. The better approach is to write a custom
TaskRepository that returns composed objects.
Step 1. Define a Custom Repository
ReactiveCrudRepository is an entry point, that offers basic CRUD functionality, but it is limited to that. In order to provide custom queries, we need to extend it with a custom repository. For that, we first create a new interface that defines a contract for custom repository, and then extend the entry repository with it:
The next step is to extend core
CustomTaskRepository, as shown below:
Step 2. Implement the Contract
Now, we can implement the aforementioned interface. Note, that implementations should have
Impl endings due to Spring DI rules. In that component, we need to inject a
DatabaseClient that is non-blocking client to handle database operations. In Spring Boot, things are pre-configured, so you just need to define a dependency and use constructor-based DI to make Spring inject it:
Step 3. Prepare SQL Queries
Fetching composed entities means that we need to use JOIN operations. PostgreSQL has 6 types of join operations, but this is out of scope of this post. Maybe in the future, I will add PostgreSQL as a topic of my blog, but not now. Here, we use the
INNER JOIN operation that returns rows that match the given condition in both tables.
In our example, we have
Project entities connected with
project_id. Take a look at the code snippet below:
Step 4. Bind Params and Execute Query
Likewise, to do it in plain JDBC, we first prepare a query and then execute it. In R2DBC, we use
DatabaseClient.execute() method for this. We also may bind some variables, like
userId. This is done using the
bind() method, which accepts two arguments: key (a name of variable in query) and a value.
Step 5. Use Mapper to Work With Results
This is cumberstone, as that is a reason why we use ORM frameworks. We need to map raw results to Java objects. For that (as well to promote reusability), we create a mapper. This design pattern, defined by Martin Fowler is used to move data between objects and a database while keeping them independent of each other and the mapper itself. The idea is displayed below:
In R2DC, reactive client mapping operation is performed by the
map() method. It accepts a normal
BiFunction that maps raw row results to corresponding Java model. We can implement it using a functional interface like this:
Next, we can add this component to our custom repository:
Step 6. Consume data
The final step is to call a terminal operation to consume the data pipeline.
DatabaseClient has three operations to work with queries:
all()= returns all rows of the result.
first()= returns the first row of the entire result.
one()= returns exactly one result and fails if the result contains more rows.
In our example, we need all entities that satisfy the query, so we use the
all() method, as shown below:
As you can see, this is not a rocket science to use R2DBC with complex composed objects without a need of ORM framework, like Hibernate. R2DBC provides a fluent API and is easy to use and to abstract database operations, so you can implement required persistence-layer logic yourself.
If you have questions regarding this post, don’t hesitate to drop a comment below or contact me. Have a nice day!
Published at DZone with permission of Yuri Mednikov . See the original article here.
Opinions expressed by DZone contributors are their own.