Merge pull request #125 from christophstrobl/issue/data-jpa-kotlin

Add spring-data sample with Kotlin
This commit is contained in:
Moritz Halbritter
2022-10-24 14:18:17 +02:00
committed by GitHub
13 changed files with 310 additions and 0 deletions

View File

@@ -0,0 +1 @@
Tests if Spring Data JPA with works with Kotlin

View 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")
}

View File

@@ -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");
});
}
}

View File

@@ -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);
}
}

View File

@@ -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 {
}

View File

@@ -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
}
}

View File

@@ -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?
}

View File

@@ -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'}"
}
}

View File

@@ -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'}"
}
}

View 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
);

View File

@@ -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() {
}
}

View File

@@ -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"