How to use ActiveJDBC with Spring Boot

Default featured post

ActiveJDBC is a Java library implements Active record design pattern. It was inspired by ActiveRecord ORM from Ruby on Rails. ActiveJDBC can be used as an alternative to Hibernate in many projects. As long as there is little to no business logic involved. In this article, I demonstrate a comprehensive example of how to use ActiveJDBC with the conjunction of Spring Boot and MySQL 8.

The example of my choice is a music streaming platform where users can create a Playlist of Songs they like. For that, we will create a set of APIs to perform basic CRUD operations on the User, Song, and Platlist resources. And to communicate with the database, we will be using ActiveJDBC.

Keep in mind that we will not have the conventional service layer. Instead we follow the Active record pattern and add most of the business logic inside of the models.

Without further ado, let’s get started.

Adding ActiveJDBC dependencies

First thing first, we need to add ActiveJDBC dependency to our project and then do some configurations to make it running.

The first paragraph of the above snippet is rather simple. We just added ActiveJDBC and MySQL connector as the project dependency. But most of the magic comes in the second part of the code where we need to have two maven plugins.

The first plugin generates models and adds them in the classpath. This is necessary, otherwise, the application throws run time exception if does not find the instruments. To get that working, we need to run a maven goal process-classes every time before starting the application which is a bit hassle. But don’t worry about it, for now, I have a fix for it which is explained later.

And the second one handles the database migration or evolution which I configured what environment, development, to run and where to find the database configuration. This plugin offers two goals for the initial table creation and gradual migration. Very similar to Liquibase. The plugin keeps track of all executes scripts in schema_version table.

Unlike the previous plugin, database plugin is optional and can be omitted. But then all the database tables and changes should be done manually.

Database schema

For the music streaming example, we need to create at least four tables, users, songs, playlists, and playlist_song. The last table is required in order to have many to many relationship between song and playlists. As one song can appear in many playlists and one playlist can have many songs.

The final database schema is look like below:

To utilize ActiveJDBC database migrator, we need to place the SQL file inside of the migrations directory under src folder. This is a predefined path.

In order for ActiveJDBC migrator to pick up the script, we need to follow a certain file naming convention such as 20190503131110_create_tables.sql. The format is Year Month Day Hour Minute Second_any description. I’ve tried other format and none has worked. Hence, I assume this is the only format to follow.

Integrating ActiveJDBC with Spring Boot ecosystem

Before starting to implement the code, we need to do one last configuration. And that’s integrating ActiveJDBC with Spring Boot. All we need to do is to create a Servlet filter that opens the database connection before hitting any endpoint and closes after that. This essentially is an optional requirement to reduce code complications. Otherwise, we need to open and close the connection each time manually.

The filter is as follows:

Keep in mind the Servlet filter is not the only option. The same results can be achieved by defining aspects or implementing HandlerInterceptor.

Creating models

Now that we have done with the configurations, it’s time to create models for our database tables. Unlike Hibernate, we don’t need to define any property in the models. Solely, extending activejdbc.Model suffices to have a functioning entity.

Keep in mind all the validations need to be done in the model under static block. ActiveJDBC provides some basic predefined validations as well as allowing to declare and inject yours.

We define our music streaming table model as below:

As described before, all the validations are done in static block. Additionally, I defined some column names as the static final variable just for convenience. And also allowing to prepare the object for persistence by accepting parameters in the constructor and setting them inside of the model internally.

The interesting method here is the merge method which I coded for enabling flawless updates which in reality is no difference than create.

It is important to know that for Many to Many relationship, we need to create a separate model. And if we don’t follow the naming convention of ActiveJDBC, like this example, we have to define the relationship via annotations in one side of the relation. For our example, we did that in the Song model by annotating the class with

@Many2Many(other = Playlist.class, join = "playlist_song", sourceFKName = "song_id", targetFKName = "playlist_id")

ActiveJDBC has a naming convention for Many to Many table which is table name_table name. However, in our example, we don’t follow that. We simply have playlist_song which according to the convention should be playlists_songs. And that is why we override the relationship.

Implementing the Spring Boots controllers

The next which is the last step is to implement the endpoints. These endpoints are not different than normal controllers just they might have some additional logic. One shortcoming of ActiveJDBC is by default it didn’t ship with many validators. One way to compensate that is to use Javax validators in DTOs to disallow end users entering undesirable inputs.

The following is the implementation of our music stream controllers:

Before finishing this post, I’d like to touch down on delete methods in ActiveJDBC. In our example we used three types of delete and each has a different functionality as below:

  • delete: deletes the sole row of the table only. Such as deleting a user or a song.
  • deleteCascade: deletes the row with all its associations entirely. For example, deleting a user row results in removing all the user’s songs and playlists.
  • deleteCascadeShallow: deletes the row and its associated foreign keys. For example, deleting a song results in deleting the foreign key of the song from playlist_song table as well. But keeps the rest of playlist untouched.

The example source code is available at GitHub at this link: https://github.com/kasramp/active-jdbc-example
All the instructions on how to run the project are provided in the readme file.

Well, that’s all about the ActiveJDBC. Of course, the example presented here was a simple one. You can do much more with ActiveJDBC and use it as a full-blown ORM. In the next article, I’ll discuss the Active Record pattern.

If you are interested about high performance data insertion with Spring Boot and Hibernete have a look at my previous tutorial at below link:
https://geekyhacker.com/2019/03/26/high-performance-data-fetching-using-spring-data-jpa-stream/