Moving from Grails to Spring Boot part 3: Object-Relational Mapping

Thomas Martin
WhozApp
Published in
3 min readDec 15, 2022

This is the last part of our series of articles about moving from Grails to Spring Boot. Services and controllers are done, let’s end this with the Object-Relational Mapping.

Photo by Campaign Creators on Unsplash

Declare id and audit fields

Grails has a convention over configuration philosophy and its ORM called GORM is no exception. To determine the identifier, just call a property id. In Spring Data you’ll need an Id annotation.

The same goes for audit fields. Grails gives you the lastUpdated and dateCreated properties to store dates of creation and modification. Spring has the following annotations: @CreationDate @LastModifiedDate @CreatedBy @LastModifiedBy. At the time we introduced the first Spring microservice, we were convinced storing the user who created or updated an entity was important. So we implemented those fields in GORM entities using the beforeInsert and beforeUpdate methods, and copied lastUpdated and dateCreated properties into createdDate and lastModifiedDate for the sake of consistency.

That means that this GORM entity class

becomes this one

Annotate transient and indexed fields

To define transient fields in grails, an array attribute calledtransients is defined into the entity class containing the names of the transient properties. Also, the indexes to set on the collection are described in the mappping static attribute.

In Spring Data, they are replaced by the @Transient, @Indexed and @CompoundIndexes annotations. @Indexed annotates a field, while @CompoundIndexes annotates the class and contains a set of annotations @CompoundIndex describing expected multi-field indexes.

Please notice that as of Spring Data MongoDB 3.0, automatic index creation is turned off by default. This is an important difference with Grails. This behavior was fine for us, but if you want to keep automatic index creation, you can by explicitly overriding the autoIndexCreation()method in your MongoConfig class. (see that Baeldung article)

Implement repositories

Then we have to implement the repositories. Since we have already written the interfaces, a good part of the work is already done. All we have to do is implement the interfaces with Spring Data, and most of the time if we follow Spring Data method naming conventions, there is nothing to do, wonderful!

In the meantime, it could be a good time to improve our data access design. In particular, we usually refactor the GET endpoints to use the Spring Data Criteria API. We found it cleaner than a bunch of if/else calling one query or another depending on the set of input parameters.

Such service method

is simplified like this

thanks to the Criteria API implemented in the repository

Up until here, you’ll probably think there is nothing spectacular in this article. And you’re right! But that’s actually a good point, it means that our strategy is efficient. The way we started our migration allows us to:

  • avoid implementing most queries thanks to derived query methods
  • avoid complex one-to-many and many-to-one mapping, because we have already eliminated such strong relationships

When the last part of a series of articles focuses on what you don’t have to do instead of what you have to do, I think that’s relaxing, isn’t it?

Photo by Jeremy Bishop on Unsplash

Thanks for reading this series of three articles, and happy migration!

--

--