-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
246 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
= Micronaut: Data JDBC | ||
|
||
Showcase demonstrating how to test JPA repositories. | ||
|
||
This showcase uses https://github.com/micronaut-projects/micronaut-test-resources[Micronaut test resources]. Have a look at `build.gradle.kts` to see an easy and painless way to automatically provide a database during tests. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
plugins { | ||
id("cnt-micronaut.micronaut-conventions") | ||
// this plugin automatically uses TestContainers to provide a postgres database for tests | ||
id("io.micronaut.test-resources") | ||
kotlin("plugin.jpa") version "1.8.22" | ||
kotlin("plugin.noarg") version "1.8.22" | ||
} | ||
|
||
dependencies { | ||
ksp("io.micronaut.data:micronaut-data-processor") | ||
implementation("io.micronaut.data:micronaut-data-hibernate-jpa") | ||
implementation("io.micronaut.flyway:micronaut-flyway") | ||
implementation("io.micronaut.sql:micronaut-jdbc-hikari") | ||
runtimeOnly("org.postgresql:postgresql") | ||
} | ||
|
||
application { | ||
mainClass.set("example.micronaut.DataApplicationKt") | ||
} | ||
|
||
noArg { | ||
annotation("example.data.jpa.model.NoArgConstructor") | ||
} |
15 changes: 15 additions & 0 deletions
15
examples/data-jpa/src/main/kotlin/example/data/jpa/model/Book.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package example.data.jpa.model | ||
|
||
import example.data.jpa.persistence.IsbnConverter | ||
import example.data.jpa.persistence.TitleConverter | ||
import jakarta.persistence.Convert | ||
import jakarta.persistence.Embeddable | ||
|
||
@NoArgConstructor | ||
@Embeddable | ||
data class Book( | ||
@field:Convert (converter = IsbnConverter::class) | ||
val isbn: Isbn, | ||
@field:Convert (converter = TitleConverter::class) | ||
val title: Title | ||
) |
9 changes: 9 additions & 0 deletions
9
examples/data-jpa/src/main/kotlin/example/data/jpa/model/Isbn.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package example.data.jpa.model | ||
|
||
private val pattern = Regex("([0-9]{3}(-)?)?[0-9]{10}") | ||
|
||
data class Isbn(val value: String) { | ||
init { | ||
require(value matches pattern) { "ISBN [$value] must match pattern [$pattern]!" } | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
examples/data-jpa/src/main/kotlin/example/data/jpa/model/NoArgConstructor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package example.data.jpa.model | ||
|
||
@Retention | ||
@Target(AnnotationTarget.CLASS) | ||
annotation class NoArgConstructor |
8 changes: 8 additions & 0 deletions
8
examples/data-jpa/src/main/kotlin/example/data/jpa/model/Title.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package example.data.jpa.model | ||
|
||
data class Title(val value: String) { | ||
init { | ||
require(value.isNotBlank()) { "Titles must not be blank!" } | ||
require(value.length <= 100) { "Titles longer than 100 characters are not allowed!" } | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
examples/data-jpa/src/main/kotlin/example/data/jpa/persistence/BookEntity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package example.data.jpa.persistence | ||
|
||
import example.data.jpa.model.Book | ||
import jakarta.persistence.Embedded | ||
import jakarta.persistence.Entity | ||
import jakarta.persistence.Id | ||
import jakarta.persistence.Table | ||
import jakarta.persistence.Version | ||
import java.util.UUID | ||
|
||
@Entity | ||
@Table(name = "books") | ||
class BookEntity( | ||
@Id | ||
val id: UUID, | ||
@Embedded | ||
var book: Book, | ||
@Version | ||
val version: Int = 0 | ||
) { | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as BookEntity | ||
|
||
if (id != other.id) return false | ||
if (book != other.book) return false | ||
if (version != other.version) return false | ||
|
||
return true | ||
} | ||
|
||
override fun hashCode(): Int { | ||
var result = id.hashCode() | ||
result = 31 * result + book.hashCode() | ||
result = 31 * result + version.hashCode() | ||
return result | ||
} | ||
} | ||
|
13 changes: 13 additions & 0 deletions
13
examples/data-jpa/src/main/kotlin/example/data/jpa/persistence/BookEntityRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package example.data.jpa.persistence | ||
|
||
import example.data.jpa.model.Title | ||
import io.micronaut.data.annotation.Query | ||
import io.micronaut.data.annotation.Repository | ||
import io.micronaut.data.jpa.repository.JpaRepository | ||
import java.util.UUID | ||
|
||
@Repository | ||
interface BookEntityRepository : JpaRepository<BookEntity, UUID> { | ||
@Query("SELECT b FROM BookEntity b WHERE b.book.title = :title") | ||
fun findByTitle(title: Title): List<BookEntity> | ||
} |
23 changes: 23 additions & 0 deletions
23
examples/data-jpa/src/main/kotlin/example/data/jpa/persistence/Converters.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package example.data.jpa.persistence | ||
|
||
import example.data.jpa.model.Isbn | ||
import example.data.jpa.model.Title | ||
import jakarta.inject.Singleton | ||
import jakarta.persistence.AttributeConverter | ||
import jakarta.persistence.Converter | ||
|
||
@Singleton | ||
@Converter | ||
class TitleConverter : AttributeConverter<Title, String> { | ||
override fun convertToDatabaseColumn(attribute: Title?) = attribute?.value | ||
|
||
override fun convertToEntityAttribute(dbData: String?) = dbData?.let(::Title) | ||
} | ||
|
||
@Singleton | ||
@Converter | ||
class IsbnConverter : AttributeConverter<Isbn, String> { | ||
override fun convertToDatabaseColumn(attribute: Isbn?) = attribute?.value | ||
|
||
override fun convertToEntityAttribute(dbData: String?) = dbData?.let(::Isbn) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
micronaut.application.name=cnt-micronaut.data-jpa | ||
datasources.default.db-type=postgres | ||
datasources.default.dialect=POSTGRES | ||
jpa.default.properties.hibernate.hbm2ddl.auto=none | ||
datasources.default.driver-class-name=org.postgresql.Driver | ||
flyway.datasources.default.enabled=true |
8 changes: 8 additions & 0 deletions
8
examples/data-jpa/src/main/resources/db/migration/V1__schema.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
CREATE TABLE books | ||
( | ||
id uuid, | ||
title text, | ||
isbn varchar(13), | ||
version bigint, | ||
PRIMARY KEY (id) | ||
) |
86 changes: 86 additions & 0 deletions
86
examples/data-jpa/src/test/kotlin/example/data/jpa/persistence/BookEntityRepositoryTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package example.data.jpa.persistence | ||
|
||
import example.data.jpa.model.Book | ||
import example.data.jpa.model.Isbn | ||
import example.data.jpa.model.Title | ||
import io.kotest.assertions.throwables.shouldThrowExactly | ||
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder | ||
import io.kotest.matchers.optional.shouldBePresent | ||
import io.kotest.matchers.shouldBe | ||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest | ||
import org.junit.jupiter.api.Disabled | ||
import org.junit.jupiter.api.Test | ||
import kotlin.random.Random.Default.nextInt | ||
import java.util.UUID | ||
|
||
@MicronautTest | ||
class BookEntityRepositoryTest( | ||
private val cut: BookEntityRepository | ||
) { | ||
|
||
@Test | ||
fun `entity can be saved`() { | ||
val entity = bookRecordEntity() | ||
|
||
val savedEntity = cut.save(entity) | ||
|
||
savedEntity.shouldBe(entity) | ||
} | ||
|
||
@Test | ||
fun `entity version is increased with every change`() { | ||
// Micronaut never returns a new Object after save, but applies all changes in place | ||
// @Version functionality is only triggered after a flush hence we call saveAndFlush | ||
val entity = bookRecordEntity() | ||
|
||
val savedEntity1 = cut.saveAndFlush(entity) | ||
savedEntity1.version.shouldBe(0) | ||
|
||
val savedEntity2 = cut.saveAndFlush(savedEntity1.changeTitle()) | ||
savedEntity2.version.shouldBe(1) | ||
|
||
val savedEntity3 = cut.saveAndFlush(savedEntity2) | ||
savedEntity3.version.shouldBe(1) | ||
|
||
val savedEntity4 = cut.saveAndFlush(savedEntity3.changeTitle()) | ||
savedEntity4.version.shouldBe(2) | ||
} | ||
|
||
@Test | ||
@Disabled | ||
// TODO Any idea whether how to test this? It is not easy, as Micronaut only has a single object | ||
// with the same Identifier. | ||
fun `entity can not be saved in lower than current version`() { | ||
val entity = bookRecordEntity() | ||
val savedEntity1 = cut.save(entity) | ||
cut.save(savedEntity1.changeTitle()) | ||
|
||
shouldThrowExactly<IllegalArgumentException> { cut.save(savedEntity1) } | ||
} | ||
|
||
@Test | ||
fun `entity can be found by id`() { | ||
val savedEntity = cut.save(bookRecordEntity()) | ||
|
||
val foundEntity = cut.findById(savedEntity.id) | ||
|
||
foundEntity.shouldBePresent().shouldBe(savedEntity) | ||
} | ||
|
||
@Test | ||
fun `entity can be found by title`() { | ||
val e1 = cut.save(bookRecordEntity("Clean Code")) | ||
cut.save(bookRecordEntity("Clean Architecture")) | ||
val e3 = cut.save(bookRecordEntity("Clean Code")) | ||
|
||
val foundEntities = cut.findByTitle(Title("Clean Code")) | ||
|
||
foundEntities.shouldContainExactlyInAnyOrder(e1, e3) | ||
} | ||
|
||
private fun bookRecordEntity(title: String = "Clean Code") = | ||
BookEntity(UUID.randomUUID(), Book(Isbn("9780123456789"), Title(title))) | ||
|
||
private fun BookEntity.changeTitle(): BookEntity = | ||
apply { book = book.copy(title = Title("Change Title #${nextInt(1_000)}")) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters