Kotlinサーバーサイドプログラミング実践開発で詰まった箇所の対応

# この記事について
Kotlin学習のため、以下の「Kotlin サーバーサイドプログラミング実践開発」を読んでいました。書籍の動作確認環境はcorretto-11でしたが、coretto-17を使用したところ複数詰まる箇所があったため共有します。

https://gihyo.jp/book/2021/978-4-297-11859-4

# MyBatisGeneratorのバージョンが違う（5章、6章）
書籍だと以下のバージョンが指定されていますが、これでは動作しませんでした。
```gradle
id("com.arenagod.gradle.MybatisGenerator") version "2.4"
```
以下のバージョンを指定すると、動作しました。
```gradle
id("com.thinkimi.gradle.MybatisGenerator") version "2.4"
```

# BookWithRentalMapperExtentions.ktがコンパイルエラーになる（6章-4）
書籍では、下記のような記述でBookWithRentalMapperを使用するExtentionsが書かれています。import含めて書籍の内容と揃えてもコンパイルエラーとなってしまいました。

```kotlin

private val columnList = listOf(...)

fun BookWithRentalMapper.select(): List<BookWithRentalRecord> {
    val selectStatement = select(columnList).from(Book, "b") {
        leftJoin(Rental, "r") {
            on(Book.id, equalTo(Rental.bookId)
        }
    }
    return selectMany(selectStatement)
}
```

そこで、以下のサイトの記述を真似て、直接SQLを書いたところ動作しました。

https://pppurple.hatenablog.com/entry/2019/08/07/223321

```kotlin
@Mapper
interface BookWithRentalMapper {

    @Select(
        "SELECT b.id, b.title, b.author, b.release_date, r.user_id, r.rental_datetime, r.return_deadline FROM book b left join rental r on b.id = r.book_id"
    )
    @Results(
        id = "BookWithRentalResult",
        value = [
            Result(column = "id", property = "id", id = true),
            Result(column = "title", property = "title"),
            Result(column = "author", property = "author"),
            Result(column = "release_date", property = "releaseDate"),
            Result(column = "user_id", property = "userId"),
            Result(column = "rental_datetime", property = "rentalDatetime"),
            Result(column = "return_deadline", property = "returnDeadline")
        ]
    )
    fun selectMany(): List<BookWithRentalRecord>
}
```

MyBatis+Kotlinで調べても公式ドキュメント含めて情報が少ない気がします。。。
あまりこの組み合わせはメジャーじゃないのでしょうか？

# SecurityConfigがコンパイルエラーになる（7章-1）
バージョンの都合か書籍のSecurityConfigの内容をそのまま使用することが出来なかったです。そこで以下のように書き換えました。

`'authorizeRequests()' はバージョン 6.1 以降は非推奨かつ廃止予定になっています`のように非推奨の書き方だらけですが、SpringSecurityの詳細を学ぶことが主目的では無いため良いとしました。

``` kotlin
package com.book.manager.bookmanager.presentation.config

import com.book.manager.bookmanager.application.service.AuthenticationService
import com.book.manager.bookmanager.application.service.BookManagerUserDetailsService
import com.book.manager.bookmanager.domain.enum.RoleType
import com.book.manager.bookmanager.presentation.handler.BookManagerAccessDeniedHandler
import com.book.manager.bookmanager.presentation.handler.BookManagerAuthenticationEntryPoint
import com.book.manager.bookmanager.presentation.handler.BookManagerAuthenticationFailurehandler
import com.book.manager.bookmanager.presentation.handler.BookManagerAuthenticationSuccessHandler
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource

@Configuration
@EnableWebSecurity
class SecurityConfig(
    private val authenticationService: AuthenticationService
) {
    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeRequests()
            .requestMatchers("/login").permitAll()
            .requestMatchers("/admin/**").hasAuthority(RoleType.ADMIN.toString())
            .anyRequest().authenticated()
            .and()
            .csrf().disable()
            .formLogin()
            .loginPage("/login")
            .loginProcessingUrl("/login")
            .usernameParameter("email")
            .passwordParameter("pass")
            .successHandler(BookManagerAuthenticationSuccessHandler())
            .failureHandler(BookManagerAuthenticationFailurehandler())
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(BookManagerAuthenticationEntryPoint())
            .accessDeniedHandler(BookManagerAccessDeniedHandler())
            .and()
            .cors()
            .configurationSource(corsConfigurationSource())

        return http.build()
    }

    @Bean
    fun authenticationProvider(): DaoAuthenticationProvider {
        val provider = DaoAuthenticationProvider()
        provider.setUserDetailsService(BookManagerUserDetailsService(authenticationService))
        provider.setPasswordEncoder(BCryptPasswordEncoder())
        return provider
    }

    private fun corsConfigurationSource(): CorsConfigurationSource {
        val corsConfiguration = CorsConfiguration()
        corsConfiguration.addAllowedMethod(CorsConfiguration.ALL)
        corsConfiguration.addAllowedHeader(CorsConfiguration.ALL)
        corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL)
        corsConfiguration.allowCredentials = true

        val corsConfigurationSource = UrlBasedCorsConfigurationSource()
        corsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration)

        return corsConfigurationSource
    }
}
```
# フロントエンドが立ち上げられなかった（7章）
おそらく私の環境起因ですが、フロントエンドサーバーを立ち上げることが出来ず、全てAPIを直接叩きました。

URLがサンプルコードから見つけられず、手打ちしていました。大変だったので主要なURLを置いておきま

```shell:ログイン
curl -i -c cookie.txt -H 'Content-Type:application/x-www-form-urlencoded' -X POST -d 'email=user@example.com' -d 'pass=password' http://localhost:8080/login
```

```shell:本の登録
curl -i -b cookie.txt -H 'Content-Type:application/json' -X POST -d '{"id": 101, "title": "New Book", "author": "yamayamaKo", "release_date": "2020-12-12"}' http://localhost:8080/admin/book/register
```

```shell:本の一覧
curl -i -b cookie.txt http://localhost:8080/book/list
```

```shell:レンタル開始
curl -b cookie.txt -H 'Content-Type:application/json' -X POST -d '{"book_id": 100}' http://localhost:8080/rental/start
```

```shell:レンタル終了
curl -b cookie.txt -X DELETE http://localhost:8080/rental/end/100
```


# 終わりに
サーバーサイドKotlinの書籍の数が少ない気がしますが、2025年2月時点だと何を読むのが良いのでしょうか？