All Projects → TouK → Krush

TouK / Krush

Licence: apache-2.0
Idiomatic persistence layer for Kotlin

Programming Languages

kotlin
9241 projects

Projects that are alternatives of or similar to Krush

Blaze Persistence
Rich Criteria API for JPA providers
Stars: ✭ 233 (+25.95%)
Mutual labels:  sql, jpa
Jeddict
Jakarta EE 8 (Java EE) & MicroProfile 3.2 application generator and modeler
Stars: ✭ 358 (+93.51%)
Mutual labels:  sql, jpa
Java Persistence Frameworks Comparison
Comparison of non-JPA SQL mapping frameworks for Java (Jooq, Spring JDBCTemplate, MyBatis, EBean, JDBI, Speedment, sql2o)
Stars: ✭ 213 (+15.14%)
Mutual labels:  sql, jpa
Minidao
轻量级JAVA持久层,类似Mybatis一样的用法,基于SpringJdbc实现更轻量
Stars: ✭ 177 (-4.32%)
Mutual labels:  sql, jpa
Hibernate Springboot
Collection of best practices for Java persistence performance in Spring Boot applications
Stars: ✭ 589 (+218.38%)
Mutual labels:  sql, jpa
Db Util
If you are using JPA and Hibernate, this tool can auto-detect N+1 query issues during testing.
Stars: ✭ 194 (+4.86%)
Mutual labels:  sql, jpa
Micronaut Data
Ahead of Time Data Repositories
Stars: ✭ 352 (+90.27%)
Mutual labels:  sql, jpa
Erupt
🚀 纯 Java 注解,快速开发 Admin 管理后台。不生成任何代码、零前端代码、零 CURD、自动建表、注解式API,支持所有主流数据库,支持自定义页面,支持多数据源,提供二十几类业务组件,十几种展示形式,支持逻辑删除,动态定时任务,前端后端分离等。核心技术:Spring Boot、JPA、Reflect、TypeScript、NG-ZORRO等。 开源不易,记得右上角点个star鼓励作者~
Stars: ✭ 421 (+127.57%)
Mutual labels:  sql, jpa
Go Sqlbuilder
A flexible and powerful SQL string builder library plus a zero-config ORM.
Stars: ✭ 539 (+191.35%)
Mutual labels:  sql, lightweight
Jooq
jOOQ is the best way to write SQL in Java
Stars: ✭ 4,695 (+2437.84%)
Mutual labels:  sql, jpa
Ebean
Ebean ORM
Stars: ✭ 1,172 (+533.51%)
Mutual labels:  sql, jpa
Interference
opensource distributed database with base JPA implementation and event processing support
Stars: ✭ 57 (-69.19%)
Mutual labels:  sql, jpa
Jplusone
Tool for automatic detection and asserting "N+1 SELECT problem" occurences in JPA based Spring Boot Java applications and finding origin of JPA issued SQL statements in general
Stars: ✭ 91 (-50.81%)
Mutual labels:  sql, jpa
Supra Api Nodejs
❤️ Node.js REST API boilerplate
Stars: ✭ 182 (-1.62%)
Mutual labels:  sql
Extd pytorch
Official EXTD Pytorch code
Stars: ✭ 177 (-4.32%)
Mutual labels:  lightweight
Tpcds Kit
TPC-DS benchmark kit with some modifications/fixes
Stars: ✭ 176 (-4.86%)
Mutual labels:  sql
Swiftdb
A modern database abstraction layer, batteries included.
Stars: ✭ 183 (-1.08%)
Mutual labels:  sql
Stackoverflow Clone
Clone project of a famous Q/A website for developers which is stackoverflow built using MySQL-Express-React-Node 🌐
Stars: ✭ 182 (-1.62%)
Mutual labels:  sql
Okta Spring Boot React Crud Example
Simple CRUD with React and Spring Boot 2.0
Stars: ✭ 176 (-4.86%)
Mutual labels:  jpa
Sqldatasource
SQL DataSource for Apollo GraphQL projects
Stars: ✭ 176 (-4.86%)
Mutual labels:  sql

Krush

Sonatype Nexus (Releases) CircleCI Sputnik

Krush is a lightweight persistence layer for Kotlin based on Exposed SQL DSL. It’s similar to Requery and Micronaut-data jdbc, but designed to work idiomatically with Kotlin and immutable data classes.

It’s based on a compile-time JPA annotation processor that generates Exposed DSL table and objects mappings for you. This lets you instantly start writing type-safe SQL queries without need to write boilerplate infrastructure code.

Rationale

  • (type-safe) SQL-first - use type-safe SQL-like DSL in your queries, no string or method name parsing
  • Minimal changes to your domain model - no need to extend external interfaces and used special types - just add annotations to your existing domain model
  • Explicit fetching - you specify explicitly in query what data you want to fetch, no additional fetching after data is loaded
  • No runtime magic - no proxies, lazy loading, just data classes containing data fetched from DB
  • Pragmatic - easy to start, but powerful even in not trivial cases (associations, grouping queries)

Example

Given a simple Book class:

data class Book(
   val id: Long? = null,
   val isbn: String,
   val title: String,
   val author: String,
   val publishDate: LocalDate
)

we can turn it into Krush entity by adding @Entity and @Id annotations:

@Entity
data class Book(
   @Id @GeneratedValue
   val id: Long? = null,
   val isbn: String,
   val title: String,
   val author: String,
   val publishDate: LocalDate
)

When we build the project we’ll have BookTable mapping generated for us. So we can persist the Book:

val book = Book(
   isbn = "1449373321", publishDate = LocalDate.of(2017, Month.APRIL, 11),
   title = "Designing Data-Intensive Applications", author = "Martin Kleppmann"
)

// insert method is generated by Krush
val persistedBook = BookTable.insert(book)
assertThat(persistedBook.id).isNotNull()

So we have now a Book persisted in DB with autogenerated Book.id field. And now we can use type-safe SQL DSL to query the BookTable:

val bookId = book.id ?: throw IllegalArgumentException()

// toBook method is generated by Krush
val fetchedBook = BookTable.select { BookTable.id eq bookId }.singleOrNull()?.toBook()
assertThat(fetchedBook).isEqualTo(book)

// toBookList method is generated by Krush
val selectedBooks = (BookTable)
   .select { BookTable.author like "Martin K%" }
   .toBookList()

assertThat(selectedBooks).containsOnly(persistedBook)

Installation

Gradle:

repositories {
    maven { setUrl("https://dl.bintray.com/kotlin/exposed") }
    maven { setUrl("https://philanthropist.touk.pl/nexus/content/repositories/releases") }
}

apply plugin: 'kotlin-kapt'
api "pl.touk.krush:annotation-processor:$krushVersion"
kapt "pl.touk.krush:annotation-processor:$krushVersion"
api "pl.touk.krush:runtime:$krushVersion"

Maven:

<repositories>
    <repository>
        <id>Exposed BinTray</id>
        <url>https://dl.bintray.com/kotlin/exposed</url>
    </repository>
    <repository>
        <id>TouK public releases</id>
        <url>https://philanthropist.touk.pl/nexus/content/repositories/releases</url>
    </repository>
</repositories>

<pluginRepositories>
    <pluginRepository>
        <id>Exposed BinTray</id>
        <url>https://dl.bintray.com/kotlin/exposed</url>
    </pluginRepository>
    <pluginRepository>
        <id>TouK public releases</id>
        <url>https://philanthropist.touk.pl/nexus/content/repositories/releases</url>
    </pluginRepository>
</pluginRepositories>

<dependencies>
    <dependency>
        <groupId>pl.touk.krush</groupId>
        <artifactId>runtime</artifactId>
        <version>${krush.version}</version>
    </dependency>
</dependencies>

...

<plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>kapt</id>
            <goals>
                <goal>kapt</goal>
            </goals>
            <configuration>
                ...
                <annotationProcessorPaths>
                    <annotationProcessorPath>
                        <groupId>pl.touk.krush</groupId>
                        <artifactId>annotation-processor</artifactId>
                        <version>${krush.version}</version>
                    </annotationProcessorPath>
                </annotationProcessorPaths>
            </configuration>
        </execution>
        ...
    </executions>
</plugin>

Dependencies

  • Exposed 0.3.0 -> 0.24.1, 0.2.0 -> 0.18.1
  • JPA annotations 2.1

Features

  • generates table mappings and functions for mapping from/to data classes
  • type-safe SQL DSL without reading schema from existing database (code-first)
  • explicit association fetching (via leftJoin / innerJoin)
  • multiple data types support, including type aliases
  • custom data type support (with @Converter), also for wrapped auto-generated ids
  • you can still persist associations not directly reflected in domain model (eq. article favorites)

However, Krush is not a full-blown ORM library. This means following JPA features are not supported:

  • lazy association fetching
  • dirty checking
  • caching
  • versioning / optimistic locking

Updating

Given following entity:

@Entity
data class Reservation(
    @Id
    val uid: UUID = UUID.randomUUID(),

    @Enumerated(EnumType.STRING)
    val status: Status = Status.FREE,

    val reservedAt: LocalDateTime? = null,
    val freedAt: LocalDateTime? = null
) {
    fun reserve() = copy(status = Status.RESERVED, reservedAt = LocalDateTime.now())
    fun free() = copy(status = Status.FREE, freedAt = LocalDateTime.now())
}

enum class Status { FREE, RESERVED }

you can call Exposed update with generated from metod to overwrite it's data:

val reservation = Reservation().reserve().let(ReservationTable::insert)

val freedReservation = reservation.free()
ReservationTable.update({ ReservationTable.uid eq reservation.uid }) { it.from(freedReservation) }

val updatedReservation = ReservationTable.select({ ReservationTable.uid eq reservation.uid }).singleOrNull()?.toReservation()
assertThat(updatedReservation?.status).isEqualTo(Status.FREE)
assertThat(updatedReservation?.reservedAt).isEqualTo(reservation.reservedAt)
assertThat(updatedReservation?.freedAt).isEqualTo(freedReservation.freedAt)

For simple cases you can still use Exposed native update syntax:

val freedAt = LocalDateTime.now()
ReservationTable.update({ ReservationTable.uid eq reservation.uid }) {
  it[ReservationTable.status] = Status.FREE
  it[ReservationTable.freedAt] = freedAt
}

Complete example

Associations

@Entity
@Table(name = "articles")
data class Article(
    @Id @GeneratedValue
    val id: Long? = null,

    @Column(name = "title")
    val title: String,

    @ManyToMany
    @JoinTable(name = "article_tags")
    val tags: List<Tag> = emptyList()
)

@Entity
@Table(name = "tags")
data class Tag(
    @Id @GeneratedValue
    val id: Long? = null,

    @Column(name = "name")
    val name: String
)

Persisting

val tag1 = Tag(name = "jvm")
val tag2 = Tag(name = "spring")

val tags = listOf(tag1, tag2).map(TagTable::insert)
val article = Article(title = "Spring for dummies", tags = tags)
val persistedArticle = ArticleTable.insert(article)

Querying and fetching

val (selectedArticle) = (ArticleTable leftJoin ArticleTagsTable leftJoin TagTable)
    .select { TagTable.name inList listOf("jvm", "spring") }
    .toArticleList()

assertThat(selectedArticle).isEqualTo(persistedArticle)

Update logic for associations not implemented (yet!) - you have to manually add/remove records from ArticleTagsTable.

Example projects

Contributors

Special thanks to Łukasz Jędrzejewski for original idea of using Exposed in our projects.

Licence

Krush is published under Apache License 2.0.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].