Merge pull request #125 from christophstrobl/issue/data-jpa-kotlin
Add spring-data sample with Kotlin
This commit is contained in:
1
data-jpa-kotlin/README.adoc
Normal file
1
data-jpa-kotlin/README.adoc
Normal file
@@ -0,0 +1 @@
|
||||
Tests if Spring Data JPA with works with Kotlin
|
||||
25
data-jpa-kotlin/build.gradle
Normal file
25
data-jpa-kotlin/build.gradle
Normal file
@@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot'
|
||||
id 'org.springframework.aot.smoke-test'
|
||||
id 'org.graalvm.buildtools.native'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
id 'org.jetbrains.kotlin.plugin.spring' version "1.7.10"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
runtimeOnly("com.h2database:h2")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
|
||||
aotSmokeTestImplementation(project(":aot-smoke-test-support"))
|
||||
aotSmokeTestImplementation("org.awaitility:awaitility:4.2.0")
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.example.data.jpa;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.smoketest.support.assertj.AssertableOutput;
|
||||
import org.springframework.aot.smoketest.support.junit.AotSmokeTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@AotSmokeTest
|
||||
class DataJpaApplicationAotTests {
|
||||
|
||||
@Test
|
||||
void insert(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
|
||||
assertThat(output).hasSingleLineContaining("insertAuthors(): author1 = Author{name='Josh Long'}")
|
||||
.hasSingleLineContaining("insertAuthors(): author2 = Author{name='Martin Kleppmann'}");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void listAllAuthors(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
|
||||
assertThat(output).hasSingleLineContaining("listAllAuthors(): author = Author{name='Josh Long'")
|
||||
.hasSingleLineContaining("Book{title='Cloud Native Java'}")
|
||||
.hasSingleLineContaining("Book{title='Reactive Spring'}")
|
||||
.hasSingleLineContaining("listAllAuthors(): author = Author{name='Martin Kleppmann'}")
|
||||
.hasSingleLineContaining("Book{title='Designing Data Intensive Applications'}");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void findById(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
|
||||
assertThat(output).hasSingleLineContaining("findById(): author1 = Author{name='Josh Long'}")
|
||||
.hasSingleLineContaining("findById(): author2 = Author{name='Martin Kleppmann'}");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryDerivedFromMethodName(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
|
||||
assertThat(output).hasSingleLineContaining("findByPartialName(): author1 = Author{name='Josh Long'}")
|
||||
.hasSingleLineContaining("findByPartialName(): author2 = Author{name='Martin Kleppmann'}");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryAnnotatedMethod(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
|
||||
assertThat(output).hasSingleLineContaining("queryFindByName(): author1 = Author{name='Josh Long'}")
|
||||
.hasSingleLineContaining("queryFindByName(): author2 = Author{name='Martin Kleppmann'}");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAll(AssertableOutput output) {
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
|
||||
assertThat(output).hasSingleLineContaining("deleteAll(): count = 0");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.example.data.jpa.kotlin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.example.data.jpa.kotlin.model.Author;
|
||||
import com.example.data.jpa.kotlin.model.Book;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Component
|
||||
class CLR implements CommandLineRunner {
|
||||
|
||||
private final AuthorRepository authorRepository;
|
||||
|
||||
CLR(AuthorRepository authorRepository) {
|
||||
this.authorRepository = authorRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(String... args) {
|
||||
var authors = insertAuthors();
|
||||
listAllAuthors();
|
||||
findById(authors);
|
||||
findByPartialName();
|
||||
queryFindByName();
|
||||
deleteAll();
|
||||
}
|
||||
|
||||
private void deleteAll() {
|
||||
this.authorRepository.deleteAll();
|
||||
long count = this.authorRepository.count();
|
||||
System.out.printf("deleteAll(): count = %d%n", count);
|
||||
}
|
||||
|
||||
private void queryFindByName() {
|
||||
Author author1 = this.authorRepository.queryFindByName("Josh Long");
|
||||
Author author2 = this.authorRepository.queryFindByName("Martin Kleppmann");
|
||||
|
||||
System.out.printf("queryFindByName(): author1 = %s%n", author1);
|
||||
System.out.printf("queryFindByName(): author2 = %s%n", author2);
|
||||
}
|
||||
|
||||
private void findByPartialName() {
|
||||
Author author1 = this.authorRepository.findByNameContainingIgnoreCase("sh lo");
|
||||
Author author2 = this.authorRepository.findByNameContainingIgnoreCase("in kl");
|
||||
|
||||
System.out.printf("findByPartialName(): author1 = %s%n", author1);
|
||||
System.out.printf("findByPartialName(): author2 = %s%n", author2);
|
||||
}
|
||||
|
||||
private void findById(List<Author> authors) {
|
||||
Author author1 = this.authorRepository.findById(authors.get(0).getId()).orElse(null);
|
||||
Author author2 = this.authorRepository.findById(authors.get(1).getId()).orElse(null);
|
||||
|
||||
System.out.printf("findById(): author1 = %s%n", author1);
|
||||
System.out.printf("findById(): author2 = %s%n", author2);
|
||||
}
|
||||
|
||||
private void listAllAuthors() {
|
||||
List<Author> authors = this.authorRepository.findAll();
|
||||
for (Author author : authors) {
|
||||
System.out.printf("listAllAuthors(): author = %s%n", author);
|
||||
for (Book book : author.getBooks()) {
|
||||
System.out.printf("\t%s%n", book);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Author> insertAuthors() {
|
||||
Author author1 = this.authorRepository.save(new Author(null, "Josh Long",
|
||||
Set.of(new Book(null, "Reactive Spring"), new Book(null, "Cloud Native Java"))));
|
||||
Author author2 = this.authorRepository.save(
|
||||
new Author(null, "Martin Kleppmann", Set.of(new Book(null, "Designing Data Intensive Applications"))));
|
||||
|
||||
System.out.printf("insertAuthors(): author1 = %s%n", author1);
|
||||
System.out.printf("insertAuthors(): author2 = %s%n", author2);
|
||||
|
||||
return Arrays.asList(author1, author2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.example.data.jpa.kotlin;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
@Configuration
|
||||
@EnableJpaRepositories
|
||||
class DataJpaConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.example.data.jpa.kotlin;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class DataJpaKotlinApplication {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
SpringApplication.run(DataJpaKotlinApplication.class, args);
|
||||
Thread.currentThread().join(); // To be able to measure memory consumption
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.example.data.jpa.kotlin
|
||||
|
||||
import com.example.data.jpa.kotlin.model.Author
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import java.util.*
|
||||
|
||||
interface AuthorRepository : JpaRepository<Author?, Long?> {
|
||||
|
||||
fun findByNameContainingIgnoreCase(partialName: String): Author?
|
||||
|
||||
@Query("SELECT a FROM Author a WHERE a.name = :name")
|
||||
fun queryFindByName(name: String): Author?
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.example.data.jpa.kotlin.model
|
||||
|
||||
import jakarta.persistence.*
|
||||
import java.util.*
|
||||
|
||||
@Entity
|
||||
class Author(@Id @GeneratedValue var id: Long?, var name: String?) {
|
||||
|
||||
@OneToMany(cascade = [CascadeType.ALL])
|
||||
var books: Set<Book>? = null
|
||||
|
||||
constructor(id: Long?, name: String?, books: Set<Book>?) : this(id, name) {
|
||||
this.books = books
|
||||
}
|
||||
|
||||
@PostPersist
|
||||
fun postPersist() {
|
||||
println("Persisted Author $id")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
if (other == null) {
|
||||
return false
|
||||
}
|
||||
val author = other as Author
|
||||
return id == author.id && name == author.name && books == author.books
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(id, name, books)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Author{name='$name'}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.example.data.jpa.kotlin.model
|
||||
|
||||
import jakarta.persistence.Entity
|
||||
import jakarta.persistence.GeneratedValue
|
||||
import jakarta.persistence.Id
|
||||
import java.util.*
|
||||
|
||||
@Entity
|
||||
class Book(@Id @GeneratedValue var id: Long?, var title: String?) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
if (other == null) {
|
||||
return false
|
||||
}
|
||||
val book = other as Book
|
||||
return id == book.id && title == book.title
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(id, title)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Book{title='$title'}"
|
||||
}
|
||||
}
|
||||
12
data-jpa-kotlin/src/main/resources/schema.sql
Normal file
12
data-jpa-kotlin/src/main/resources/schema.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
create table author
|
||||
(
|
||||
id bigint auto_increment primary key,
|
||||
name varchar not null
|
||||
);
|
||||
|
||||
create table book
|
||||
(
|
||||
id bigint auto_increment primary key,
|
||||
author bigint not null references author (id),
|
||||
title varchar not null
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.example.data.jpa.kotlin;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class DataJpaKotlinApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,6 +59,7 @@ include "data-cassandra-reactive"
|
||||
include "data-jdbc-h2"
|
||||
include "data-jdbc-postgresql"
|
||||
include "data-jpa"
|
||||
include "data-jpa-kotlin"
|
||||
include "data-mongodb"
|
||||
include "data-mongodb-reactive"
|
||||
include "data-r2dbc"
|
||||
|
||||
Reference in New Issue
Block a user