{"id":13921,"date":"2019-12-30T12:35:34","date_gmt":"2019-12-30T11:35:34","guid":{"rendered":"https:\/\/medium.com\/p\/ab8e5c6de72d"},"modified":"2023-01-31T13:55:59","modified_gmt":"2023-01-31T12:55:59","slug":"announcing-krush%e2%80%8a-%e2%80%8aidiomatic-persistence-layer-for-kotlin-based-on-exposed","status":"publish","type":"post","link":"https:\/\/touk.pl\/blog\/2019\/12\/30\/announcing-krush%e2%80%8a-%e2%80%8aidiomatic-persistence-layer-for-kotlin-based-on-exposed\/","title":{"rendered":"Announcing Krush &#8211; idiomatic persistence layer for Kotlin, based on Exposed"},"content":{"rendered":"<h3 id=\"tldr\"><strong>TL;DR<\/strong><\/h3>\n<p>We&#8217;ve released a persistence library for Kotlin, you can find it <a href=\"https:\/\/github.com\/TouK\/krush\">on our Github<\/a>. It\u2019s a JPA-to-Exposed SQL DSL generator.<\/p>\n<h3 id=\"the-state-of-persistence-in-kotlin\">The state of persistence in\u00a0Kotlin<\/h3>\n<p>One of the key decisions that helped Kotlin gain massive popularity was to reuse Java ecosystem instead of inventing it\u2019s 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.<\/p>\n<p>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 <a href=\"https:\/\/spring.io\/guides\/tutorials\/spring-boot-kotlin\/#_persistence_with_jpa\">official Spring JPA guide<\/a> for Kotlin uses mutable classes and properties which is not really idiomatic for Kotlin where you want to use immutable data classes whenever it\u2019s possible. <!--more Read more --><\/p>\n<blockquote class=\"twitter-tweet\" data-conversation=\"none\" data-align=\"center\" data-dnt=\"true\"><p>Big update of the official <a title=\"Twitter profile for @SpringBoot\" href=\"http:\/\/twitter.com\/SpringBoot\">@SpringBoot<\/a> + <a title=\"Twitter profile for @Kotlin\" href=\"http:\/\/twitter.com\/Kotlin\">@Kotlin<\/a> tutorial: &#8211; #Mockk instead of Mockito &#8211; Follows <a title=\"Twitter profile for @jbnizet\" href=\"http:\/\/twitter.com\/jbnizet\">@jbnizet<\/a> JPA guidelines (var instead of val, no data class) &#8211; Maven + Gradle build &#8211; Nullable instead of Optional for CrudRepository &#8211; Spring Boot 2.1.3 <a href=\"https:\/\/t.co\/B02Y8TugQf\" rel=\"nofollow\">https:\/\/t.co\/B02Y8TugQf<\/a><\/p>\n<p>\u2014\u200a<a href=\"https:\/\/twitter.com\/sdeleuze\/status\/1096423498325651459\">@sdeleuze<\/a><\/p><\/blockquote>\n<p>There are some other options, which can be used safely with Kotlin and data classes, like <a href=\"https:\/\/docs.spring.io\/spring-data\/jdbc\/docs\/current\/reference\/html\/#mapping.kotlin\">Spring Data JDBC<\/a>\u200a\u2014\u200ainteresting approach based on pure JDBC, embracing DDD and aggregate root concepts or <a href=\"https:\/\/micronaut-projects.github.io\/micronaut-data\/latest\/guide\/#sqlMapping\">Micronaut Data JDBC<\/a>\u200a\u2014\u200aif you\u2019re not tied to Spring ecosystem. But they\u2019re both relatively new, not mature yet and miss another idiomatic Kotlin feature\u200a\u2014\u200aa DSL for making SQL\u00a0queries.<\/p>\n<h3 id=\"dsl-for-sql-queries\">DSL for SQL\u00a0queries<\/h3>\n<p>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 <a href=\"https:\/\/github.com\/Kotlin\/anko\">anko<\/a> library for constructing complex view layouts for Android apps. In Spring\/JPA the default approach to SQL queries are <a href=\"https:\/\/docs.spring.io\/spring-data\/jpa\/docs\/current\/reference\/html\/#jpa.query-methods.query-creation\">query methods<\/a>, 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 <a href=\"http:\/\/requery.io\/\">Requery<\/a> or\u00a0<a href=\"https:\/\/github.com\/JetBrains\/Exposed\">Exposed<\/a>.<\/p>\n<h3 id=\"requery\">Requery<\/h3>\n<p><a href=\"https:\/\/requery.io\/\">Requery<\/a> 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\u00a0\u201cmodel\u201d.<\/p>\n<p>So given a Book interface:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">@Entity\r\n@Table(name = \"books\")\r\n\r\ninterface Book : Persistable {\r\n    @get:Key @get:Generated\r\n    val id: Long\r\n\r\n    val isbn: String\r\n    val author: String\r\n    val title: String\r\n\r\n    val publishDate: LocalDate\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>You can instantiate and persist it by using generated BookEntity class:<\/p>\n<figure>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">\/\/given\r\nval book = BookEntity().apply {\r\n    setIsbn(\"1449373321\")\r\n    setPublishDate(LocalDate.of(2017, Month.APRIL, 11))\r\n    setTitle(\"Designing Data-Intensive Applications\")\r\n    setAutor(\"Martin Kleppmann\")\r\n}\r\n\r\n\/\/ when\r\nval persistedBook = dataStore.insert(book)<\/pre>\n<\/figure>\n<p>And the use SQL DSL to fetch data and map the results back to entities:<\/p>\n<figure>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">\/\/ then\r\nval books = dataStore.select(Book::class).where(Book::id eq  book.id).get().toList()\r\n\r\nassertThat(books).containsExactly(persistedBook)<\/pre>\n<\/figure>\n<p>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\u00a0support:<\/p>\n<ul>\n<li>immutable interface approach needs to be backed up with this generated, mutablexxxEntity class<\/li>\n<li>there are <a href=\"https:\/\/github.com\/requery\/requery\/wiki\/Immutable-types\">some restrictions<\/a>: e.g. you cannot use them to map relations to other entities (just foreign keys by\u00a0ids)<\/li>\n<li>@Generated also doesn\u2019t work for <a href=\"https:\/\/github.com\/requery\/requery\/wiki\/Kotlin\">ids in data\u00a0classes<\/a>.<\/li>\n<\/ul>\n<p>You can check example project using Requery in <a href=\"https:\/\/github.com\/TouK\/krush-example\/tree\/requery\">requery branch<\/a> of <a href=\"https:\/\/github.com\/TouK\/krush-example\">krush-example<\/a> project on\u00a0GitHub.<\/p>\n<h3 id=\"exposed\">Exposed<\/h3>\n<p>Another approach which given you rich SQL DSL support is <a href=\"https:\/\/github.com\/JetBrains\/Exposed\">Exposed<\/a>\u200a\u2014\u200aa 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:<\/p>\n<figure>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">object  BookTable : Table(\"books\") {\r\n    val id: Column&lt;Long&gt; = long(\"id\").promaryKey().autoIncrement()\r\n    val isbn: Column&lt;String&gt; = varchar(\"isbn\". 255)\r\n    val autor: Column&lt;String&gt; = varchar(\"author\". 255)\r\n    val title: Column&lt;String&gt; = varchar(\"title\". 255)\r\n    val publishDate: Column&lt;LocalDate&gt; = date(\"publishDate\")\r\n}<\/pre>\n<\/figure>\n<p>Then you can refer to these Column properties to create type-safe queries and map results using Kotlin collections API:<\/p>\n<figure>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">val titles: List&lt;String&gt; = BookTable\r\n        .select { BookTable.author like \"Martin K%\" }\r\n        .map { it[BookTable.title] }<\/pre>\n<\/figure>\n<p>As you can see, Exposed is not a full-blown ORM\u200a\u2014\u200athere is no direct mapping to\/from your domain classes into these Table objects, but it\u2019s 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\u2019s extension functions. We wrote some time ago <a href=\"https:\/\/touk.pl\/blog\/2019\/02\/12\/how-we-use-kotlin-with-exposed-at-touk\/\">this article<\/a> about our approach to using Exposed in our projects.<\/p>\n<h3 id=\"krush\">Krush<\/h3>\n<p>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\u2019s done in Requery. This ended with building a library called <a href=\"https:\/\/github.com\/TouK\/krush\"><strong>Krush<\/strong><\/a>, which we\u2019re announcing today\u00a0;)<\/p>\n<p><a href=\"https:\/\/github.com\/TouK\/krush\">Krush<\/a> consist of two components:<\/p>\n<ul>\n<li><strong>annotation-processor<\/strong> which generates Exposed mappings by reading (a subset of) standard JPA annotations found on entity\u00a0classes<\/li>\n<li><strong>utility functions<\/strong> for persisting entities and mapping from\/to Exposed\u00a0objects<\/li>\n<\/ul>\n<p>So given this\u00a0entity:<\/p>\n<figure>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">@Entity\r\n@Table(name = \"books\")\r\n\r\ndata class Book(\r\n    @Id @GeneratedValue\r\n    val id: Long? = null,\r\n\r\n    val isbn: String,\r\n    val author: String,\r\n    val title: String,\r\n    val publishDate: LocalDate\r\n)<\/pre>\n<\/figure>\n<p>Krush will generate BookTable object which allows to persist it like\u00a0this:<\/p>\n<figure>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">\/\/given\r\nval book = Book(\r\n        isbn = \"1449373321\", publishDate = LocalDate.of(2017, Month.APRIL, 11),\r\n        title = \"Designing Data-Intensive Applications\", author = \"Martin Kleppmann\"\r\n)\r\nval persistedBook = BookTable.insert(book)\r\nassertThat(persistedBook.id).isNotNull()<\/pre>\n<\/figure>\n<p>And write queries using type-safe DSL just like you were using plain\u00a0Exposed:<\/p>\n<figure>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\">val bookId = book.id ?: throw IllegalargumentException( )\r\nval fetchedBook = BookTable.select { BookTable.id eq bookId }.singleOrNull()?.toBook( )\r\nassertThat(fetchedBook).isEqualTo(book)\r\n\r\nval selectedBooks = BookTable\r\n        .select { BookTable.author like \"Martin Kx\" }\r\n        .toBookList()\r\n\r\nassertThat(selectedBooks).containsOnly(book)<\/pre>\n<\/figure>\n<p>That\u2019s it! You can find more details and supported features in the README of <a href=\"https:\/\/github.com\/TouK\/krush\">Krush repository<\/a> or in <a href=\"https:\/\/github.com\/TouK\/krush-example\">some<\/a> <a href=\"https:\/\/github.com\/TouK\/kotlin-exposed-realworld\">example projects<\/a>.<\/p>\n<p>Enjoy! Looking for feedback from the community!<\/p>\n","protected":false},"excerpt":{"rendered":"We&#8217;ve released a persistence library for Kotlin, you can find it on our Github. It\u2019s a JPA-to-Exposed SQL DSL generator.\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[664,687,665,51,556],"class_list":{"0":"post-13921","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-development-design","7":"tag-annotation-processor","8":"tag-db","9":"tag-jdbc","10":"tag-jpa","11":"tag-kotlin"},"_links":{"self":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13921","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/comments?post=13921"}],"version-history":[{"count":17,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13921\/revisions"}],"predecessor-version":[{"id":15084,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13921\/revisions\/15084"}],"wp:attachment":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/media?parent=13921"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/categories?post=13921"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/tags?post=13921"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}