Spring Boot Persistence Theory. Common practices to work with databases

In the previous post, we created a simple Spring Boot application. However, the database layer was omitted. The reason for that is the fact that the introduction of a database layer is a complex task. At a first glance, only the specification of few properties is required to add a database to Spring Boot. Nevertheless, for local development and production usage, additional actions are required. In this post, we will look at Spring Boot Persistence.

Adding persistence layer to Spring Boot requires a Spring Boot Data JPA starter dependency, specific JDBC driver (e.g. for MySQL), and a few properties in application.yml. This dependency will allow us to use Hibernate with Spring Data. Hibernate is used for object-relational mapping. Spring Data is responsible for the quick creation of repositories that operate with the database.

It can be enough for prototyping or pet projects. To optimize our operations to the database connection pooling has to be used. HikariCP can be used for this purpose. It introduces effective connection pooling into the data source. We only need to add a dependency. Spring Boot will configure everything for us. If we are not satisfied with default connection pooling options, they can be changed in application.yml.

Open Session In View means that the whole request will be automatically decorated with transactions. It is the default behavior of Spring Boot. On the one hand, it is useful because you shouldn’t care about transaction demarcation and lazy loading. On the other hand, when project is starting to grow, it can be potential performance issues. It is better to disable this option in application.yml at the beginning of the project.

It is obvious that to work with RDBMS you should have one installed on your laptop.

At a first glance, the only thing you should do is install it on your machine. However, this approach brings a lot of drawbacks. Firstly, you can work with multiple projects at the same time. As a result, you have to install multiple RDBMS systems on your machine at the same time. Secondly, you can need multiple versions of the same database (e.g. MySQL 5.6 and 5.7). Thirdly, it can be a case when the database has to be initialized in some way. It will require a lot of manual work and will not be convenient and efficient.

The better way is to use Docker. As a result, your database will be specified in a docker-compose.yml file in a project repository. You run a simple command and receive the same initialized database which your team members use.

Using Docker the whole application can be ran with a single command. It speeds up the development.

You might think that table management can be done directly in the database using some client. However, it cannot be done this way in a production application. The application has various versions and is deployed in multiple environments. Team members should know the actual state of the database. In some cases, developers can create conflicting versions.

Database management is done with some migration management tools. Flyway or Liquibase can be used.

Spring Data repositories can be used in most cases. However, in some situations different approaches are applicable.

  • Hibernate entity graphs can be used to optimize database queries.
  • Spring JPA Specifications and Query DSL are responsible for more complex queries.
  • JOOQ is a good choice for writing native queries.

Tests can be different on different projects. However, they should be presented in any project in some form.

Unit Tests

Let’s start with unit tests. The database layer is mocked disregard used technology. In our example, repository operations with Spring Data will be mocked (in most cases with Mockito).

Integration Tests

Integration tests requires database. There are two main approaches here.

In-memory database. One of in-memory databases can be used in tests e.g. H2. Actually, we test our code on a different database in this case. In some cases, tests can be successful but not a production code.

Target database. With this approach target database is used. The Testcontainers is a good choice that uses Docker underneath.

Test scenarios should have specific data before execution. It can be achieved with spring test utilities or e.g. Database Rider.

End to End Tests

They are similar to integration tests but are executed disregard of the technology used. In our case, REST API should be tested without the knowledge of the actual implementation. Docker can be helpful here.

To sum up, in this post the common problems of the persistence layer were discussed. The application of this theory will be done in future posts.

Originally published at https://datamify.com on May 16, 2021.