Kotlin Springboot -- JPA, hibernate, h2 DB の初期設定に失敗
Tags: jpa, h2, kotlin, spring
Published: 2022-10-12 15:26:29

## 参考

https://spring.io/guides/tutorials/spring-boot-kotlin/

Spring 公式のチュートリアル

---


## H2 DB を使うメリット

https://m12i.hatenablog.com/entry/2014/10/29/010801

永続的なストレージとしては機能しない。
つまりアプリの起動のたびに初期化される。
組み込みとして追加できる。
( なので psql サーバーを Docker で立てて )
URL ( と認証 ) 書いて接続する手間が省ける。

と書いてあった。

H2 はインメモリの DB らしい。
依存関係を書けばそのまま使えるらしいので導入してみる

---

# Gradle で JPA を同居させて Spring を動かす

Maven の場合は POM に書く

## build.gradle.kts の plugins に jpa を追加

```kotlin
  kotlin("plugin.jpa") version "1.6.21"
```

プラグインとして jpa を使えるようにする

---

## Null でバグる issue 対策

```kotlin
tasks.withType<KotlinCompile> {
  kotlinOptions {
    freeCompilerArgs = listOf("-Xjsr305=strict")
  }
}
```

コンパイラに指定のオプションをつける

---

## dependencies に h2 を追加

```kotlin
dependencies {
// ...
  runtimeOnly("com.h2database:h2")
}
```

runtime だけ h2 を動かす
ランタイムっていうのが SpringBootApp が起動して落ちるまでの間と解釈する。

---

## application.properties に H2 用の設定を追加

```php
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.properties.hibernate.globally_quoted_identifiers_skip_column_definitions = true
```

application.properties に h2 の設定が必要。

https://m12i.hatenablog.com/entry/2014/10/29/010801

Spring の Hibernate のエンティティマネージャーにこの変数と値が渡されるらしい。

調べたけど全くわからないので、この
globally_quoted_identifiers
globally_quoted_identifiers_skip_column_definitions
2 つの値の true の設定はおまじないとしておく。

---

## Controller とテストを書く

省略

---

# H2 DB を(インメモリの範囲で)永続化させる

## allopen をプラグインに追加

エンティティを全てオープンにしておかないと
lazy-fetch が使えないので永続化できないらしい。

```kotlin
  kotlin("plugin.allopen") version "1.6.21"
```

allopen を plugins に追加して

```kotlin
allOpen {
	annotation("javax.persistence.Entity")
	annotation("javax.persistence.Embeddable")
	annotation("javax.persistence.MappedSuperclass")
}
```

Entity, Embeddable, MappedSuperclass, 
これらの永続化用のアノテーションも import できるように読み込んでおく

---

## Entity を作成

これを元に DB のテーブルが作成されると予想する。

Controller と並列に Entities.kt というファイルを作り

```kotlin
import javax.persistence.GeneratedValue
import javax.persistence.Id

class Article (
    var title: String,
    var slug: String = title.toSlug(),
    @Id
    @GeneratedValue
    var id: Long? = null)
```

自動生成の ID と String の Title だけを作る

Javax の Persisitence ( 耐久? ) のライブラリを使う

---

## Repositiry を作成

JPA を使った Driver だと解釈する。

```kotlin
import org.springframework.data.repository.CrudRepository

interface ArticleRepository : CrudRepository<Article, Long> {
  fun findByTitle(title: String): Article?
}
```

同じく Controller 並列に Repository を作成
findBy{ColumnName}(ColumnName: type) とすることで
JPA を使って値が取れると解釈する

---

## Repository のテストを作成できない

テストでデータをいれるようだ
しかし読み込みが全くうまく行かなかった

```kotlin
＠DataJpaTest
Class RepositoryTests @Autowired constructor (
    val entityManager: TestEntityManager,
    val articleRepository: ArticleRepository) {
        @Test
        fun `When findByIdOrNull then return Article`() {
            val article = Article("Spring Framework 5.0 goes GA", "2")
            entityManager.persist(article)
            entityManager.flush()
            val found = articleRepository.findByTitle(article.title)
            assertThat(found).isEqualTo(article)
        }
    }
)
```

まず、import たちのちょくごにこのアノテーションを書いても
Expecting a top level defintion と
トップに宣言しろとエラーがでるし

`@Test` もエラーがでる関数の書き方が違うと。
関数名がないと言われる。

https://stackoverflow.com/a/51894673

`@SpringbootApplication` が
main で呼ばれているからトップでないと言われているかもしれない？
要検証