We've released a persistence library for Kotlin, you can find it on our Github. It’s a JPA-to-Exposed SQL DSL generator.
The state of persistence in Kotlin
One of the key decisions that helped Kotlin gain massive popularity was to reuse Java ecosystem instead of inventing it’s own. This means that you can safely use Kotlin as a primary language for a project developed using any popular Java stack like Spring Boot and built with Java build tool like Maven. What this also means is that natural choice for persistence layer in Kotlin is Spring Data with JPA 3 with Hibernate as an implementation.
However, JPA, which highly relies on mutable objects and dirty checking, may not look like pure Kotlin, which tries to embrace functional programming and immutability. The official Spring JPA guide for Kotlin uses mutable classes and properties which is not really idiomatic for Kotlin where you want to use immutable data classes whenever it’s possible.
Big update of the official @SpringBoot + @Kotlin tutorial: - #Mockk instead of Mockito - Follows @jbnizet JPA guidelines (var instead of val, no data class) - Maven + Gradle build - Nullable instead of Optional for CrudRepository - Spring Boot 2.1.3 https://t.co/B02Y8TugQf
There are some other options, which can be used safely with Kotlin and data classes, like Spring Data JDBC — interesting approach based on pure JDBC, embracing DDD and aggregate root concepts or Micronaut Data JDBC — if you’re not tied to Spring ecosystem. But they’re both relatively new, not mature yet and miss another idiomatic Kotlin feature — a DSL for making SQL queries.
DSL for SQL queries
Another thing that made Kotlin really powerful and popular is its ability to construct Domain Specific Languages using features like property reference, operators, infix and extension functions. For example, for Android development there is excellent anko library for constructing complex view layouts for Android apps. In Spring/JPA the default approach to SQL queries are query methods, where you use special naming convention of methods in repository interfaces. The method names are parsed at runtime to provide required SQL queries and mapping. The naming convention is supported by IntelliJ Idea and other IDEs and works well in simple cases, but may be not flexible enough when you want complex queries e.g. with some conditions based on dynamic filters. If you want to use a true, type-safe, composable and idiomatic Kotlin SQL DSL, you can try to use other libraries, like Requery or Exposed.
Requery is a lightweight persistence library for Java and Kotlin with RxJava and Java 8 streams support. It uses annotations (both custom and JPA) to process your entities and generate some infrastructure code called “model”.
So given a Book interface:
You can instantiate and persist it by using generated BookEntity class:
And the use SQL DSL to fetch data and map the results back to entities:
This was really close to our needs! We like the idea of having annotations on the entities combined with the rich SQL DSL. Also the RxJava bindings and lazy Kotlin sequences support looks promising. On the other side, there are few minor issues related to immutable classes support:
- immutable interface approach needs to be backed up with this generated, mutablexxxEntity class
- there are some restrictions: e.g. you cannot use them to map relations to other entities (just foreign keys by ids)
- @Generated also doesn’t work for ids in data classes.
Another approach which given you rich SQL DSL support is Exposed — a Kotlin-only persistence layer maintained by the JetBrains team. It comes in two flavors: active-record DAO and lightweight SQL DSL. As we are not the fans of active records, we tried the SQL DSL flavor. It works by creating additional mapping code using Kotlin objects and extension functions:
Then you can refer to these Column properties to create type-safe queries and map results using Kotlin collections API:
As you can see, Exposed is not a full-blown ORM — there is no direct mapping to/from your domain classes into these Table objects, but it’s not hard to write simple mapping functions for that. You can also benefit from Kotlin null-types support and write bindings for your own types by using Kotlin’s extension functions. We wrote some time ago this article about our approach to using Exposed in our projects.
We really like the Kotlin-first feeling combined with great flexibility of Exposed, but at some time we were tired of writing these table mappings manually. We thought that it would be nice to generate them from JPA-compatible annotations, in similar way it’s done in Requery. This ended with building a library called Krush, which we’re announcing today ;)
Krush consist of two components:
- annotation-processor which generates Exposed mappings by reading (a subset of) standard JPA annotations found on entity classes
- utility functions for persisting entities and mapping from/to Exposed objects
So given this entity:
Krush will generate BookTable object which allows to persist it like this:
And write queries using type-safe DSL just like you were using plain Exposed:
Enjoy! Looking for feedback from the community!