Migrating JPA models to Kotlin

John Wood
John Wood
Nov 1 · 3 min read

We’ve gone through the process of migrating our JPA based app from Java to Kotlin. Here are some of the gotchas we found along the way.

I’ll illustrate some of these by converting a simple JPA Java class:

If we use IntelliJ’s automatic conversion tool, we end up with this:

There are a few problems with this. Firstly, our name field is nullable which doesn’t match the DB schema definition. But if we change it to var name: String then, you’ll get a compiler error because it hasn’t been initialised.

Secondly, there is an ugly = 0 assignment.

So, let’s fix these first:

Note that we’re using Kotlin’s compact primary constructor notation.

Open-ness

If you tried to use this class, it would fail at runtime because many JPA implementations (such as Hibernate) require classes and methods to be open (non final), so they can be overridden dynamically.

You could add the open keyword to your code, but, fortunately the Kotlin community has a better option: the All Open plugin. This makes certain classes and functions open at runtime.

(Incidentally, open classes are also required for Spring, so the plugin is useful for that too.)

No-args Constructor

The above code would also fail for a different reason: JPA requires a no-args constructor. But, we can’t add a no-args constructor because then the fields would never be initialised.

You could use lateinit to solve this, which would tell the Kotlin compiler that “someone” will initialise it later. However, this makes the code more bloated and removes a useful compile time check.

You could set your fields as nullable, but this defeats a major benefit of Kotlin

Similar to above, the Kotlin community have provided a “No-arg plugin” which magically adds in constructors to Kotlin classes at runtime which is available using reflection (which is how JPA works). The constructor is not available to Kotlin code, so doesn’t break the contract of your non-null fields.

The important thing to note, however, is that this bypasses any Kotlin protections so you may get runtime errors if your database schema doesn’t match the fields.

For example, if a database row has the name column as NULL, then you would get a null pointer exception at runtime when JPA loaded this row into a Customer instance (see more on that below).

Here is what the maven.pom XML looks like with both plugins activated:

Consistency between null-ness and DB schema

There is now a consistency problem between the null-ness of your Kotlin fields and the @Basic(optional=false) / @Column(nullable=false) annotations.

(There is also a consistency problem between the annotations and your actual database schema, but that’s a problem outside of the scope of this article).

One thing we found really useful was having a way of checking that a programmer hadn’t made a mistake here. We came up with a unit test which scans the classpath for all JPA classes and uses reflection to check them for consistency.

This uses Spring’s ClassPathScanningCandidateComponentProvider to search for classes, but if you don’t want to use that, there may be other alternatives.

Full code follows:

Final thoughts

The migration process was actually pretty straight forward once the above problems were solved.

It also forced us to be much stricter with the “null-ness” of both our database schema and our models, which has surfaced a few very subtle bugs and has generally made our code more robust.

Finally, when converting a legacy project to Kotlin, we recommend actually starting with your models first. Doing this causes a ripple effect: as you convert other classes, the null/non-null types get automatically propagated through your code.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade