This document presents an approach for replacing JPA to enable asynchronous and non-blocking access to relational databases, optimized for large-scale data processing.
GenericPropertyMatchers.ignoreCase()) .withIgnoreNullValues() val example = Example.of(Person("Walter", "WHITE", 0), matcher) repository.findAll(example).asFlow().toList() shouldContainSame listOf(walter, flynn) interface PersonRepository: ReactiveCrudRepository<Person, Int>, ReactiveQueryByExampleExecutor<Person> data class Person( val firstname: String, val lastname: String, val age: Int, ): Serializable { @Id var id: Int? = null val hasId: Boolean get() = id != null }
null, @Column("content") val content: String? = null, @Id val id: Long? = null, ): Serializable @Repository class PostRepository( private val client: DatabaseClient, private val operations: R2dbcEntityOperations, private val mappingR2dbcConverter: MappingR2dbcConverter, ) { suspend fun count(): Long = operations.coCountAll<Post>() fun findAll(): Flow<Post> = operations.coSelectAll() suspend fun findOneById(id: Long): Post = operations.coFindOneById(id) suspend fun findOneByIdOrNull(id: Long): Post? = operations.coFindOneByIdOrNull(id) suspend fun findFirstById(id: Long): Post = operations.coFindFirstById(id) suspend fun findFirstByIdOrNull(id: Long): Post? = operations.coFindFirstByIdOrNull(id) suspend fun deleteAll(): Long = operations.coDeleteAll<Post>() suspend fun save(post: Post): Post = operations.coInsert(post) suspend fun init() { save(Post(title = "My first post title", content = "Content of my first post")) save(Post(title = "My second post title", content = "Content of my second post")) } } @Table("comments") data class Comment( @Column("content") val content: String? = null, @Column("post_id") val postId: Long? = null, @Id val id: Long? = null, ): Serializable
book with fetch join`() = runSuspendIO { val sql = "SELECT b FROM Book b LEFT JOIN FETCH b.author a" val books = sf.withSessionSuspending { session -> session.createSelectionQuery(sql, Book::class.java).resultList.awaitSuspending() } books.forEach { println("book=$it, author=${it.author}") } books shouldHaveSize 3 } select b1_0.id, b1_0.author_id, a1_0.id, a1_0.name, b1_0.isbn, b1_0.published, b1_0.title from books b1_0 left join authors a1_0 on a1_0.id=b1_0.author_id
AliasableSqlTable<PersonTable>("Person", PersonSchema::PersonTable) { val id = column<Int>("id") val firstName = column<String>("first_name") val lastName = column<String>("last_name") val birthDate = column<LocalDate>("birth_date") val employed = column<Boolean>("employed") val occupation = column<String>("occupation") val addressId = column<Int>("address_id") } class AddressTable: AliasableSqlTable<AddressTable>("Address", PersonSchema::AddressTable) { val id = column<Int>(name = "address_id") val streetAddress = column<String>(name = "street_address") val city = column<String>(name = "city") val state = column<String>(name = "state") }
• Lightweight ORM • Provide two layers of data access • Typesafe SQL wrapping DSL • Lightweight Data Access Object • Support Coroutines (CPU-Bounded Async/Non-Blocking)
{ companion object: IntEntityClass<Movie>(Movies) var name by Movies.name var producerName by Movies.producerName var releaseDate by Movies.releaseDate override fun equals(other: Any?): Boolean = idEquals(other) override fun hashCode(): Int = idHashCode() override fun toString(): String = toStringBuilder() .add("name", name) .add("producerName", producerName) .add("releaseDate", releaseDate) .toString() } class Actor(id: EntityID<Int>): IntEntity(id), Serializable { companion object: IntEntityClass<Actor>(Actors) var firstName by Actors.firstName var lastName by Actors.lastName var dateOfBirth by Actors.dateOfBirth override fun equals(other: Any?): Boolean = idEquals(other) override fun hashCode(): Int = idHashCode() override fun toString(): String = toStringBuilder() .add("firstName", firstName) .add("lastName", lastName) .add("dateOfBirth", dateOfBirth) .toString() }