Blog

Database Routing in Spring Boot

Sometimes when building services, we might need to connect to different data sources at the same time. This can be done while the service is running with just one instance.

Spring offers a helpful tool called AbstractRoutingDataSource that allows us to choose the right data source dynamically. A great example of this is with databases that have separate readers and writers. For instance, in Amazon’s Aurora MySQL database, you might have a master database for writing data and separate replica databases for reading it. The replicas eventually catch up to the master, keeping the data consistent.

DATABASE OTIMIZATION

Let’s take a look at the essential components needed to implement database routing:

You’ll need Spring JDBC and MySQL Connector libraries to connect to your database (e.g., MySQL) and for Spring Boot to manage data access. The provided code includes these dependencies.

A central piece is a custom DataSource class that extends Spring’s AbstractRoutingDataSource. This class determines which database (master or replica) to use for each request.

The logic for choosing the database is typically based on factors like read-only operations (reads go to replicas for better performance). In this example, a determineCurrentLookupKey method is implemented to decide the database based on the presence of the @Transactional(readOnly=true) annotation.

A Spring configuration class is created to define the different databases (master and replica) and their configurations. It uses a map to link database connection details with keys returned by the determineCurrentLookupKey method.

A default database is also specified in case the routing logic doesn’t provide a clear choice. Additionally, configurations for connection pooling and read-only behavior for replicas are set up.

This file holds the actual connection details (URLs, usernames, passwords) for both the master and replica databases.

After setting up these components, our application should function as expected. However, there’s a crucial consideration regarding the behavior of @Transactional with readOnly=true, which still directs calls to the writer instance due to Spring’s Transaction Manager implementation. To override this behavior, we need to create a custom Transaction Manager implementation and inject it into the Spring context.

To see this setup in action, you can download the repository and make the necessary adjustments according to your requirements.

Example Repository – https://github.com/kanakharaharsh/db-routing-spring-boot/tree/main