前回
Micronaut + Kotlin のプロジェクトにてMyBatisを使ってデータベースからデータ取得してみる
前回のプロジェクトを拡張し、データベースにデータ登録してみたいと思います。
実際に作成したリポジトリはこちらになります。
Mapper
Interfaceの作成
BookMapper.ktに次のメソッドの宣言を追加します。
-
insert
:データの登録(Insert文の実行)を行います。 -
update
:データの更新(Update文の実行)を行います。
package example.mapper
import example.model.Book
import org.apache.ibatis.annotations.Mapper
@Mapper
interface BookMapper {
fun findAll(): List<Book>
fun findById(id: Int): Book
fun insert(book: Book)
fun update(book: Book)
}
SQLの作成
実行するSQL本体を定義します。
今回のサンプルではBookMapper.xmlに定義します。
<insert id="insert" parameterType="example.model.Book">
INSERT INTO book (
name,
publisher,
publication_date,
created_at,
updated_at
) VALUES (
#{name},
#{publisher},
#{publicationDate},
current_timestamp,
current_timestamp
);
</insert>
<update id="update" parameterType="example.model.Book">
UPDATE book
SET
name = #{name},
publisher = #{publisher},
publication_date = #{publicationDate},
updated_at = current_timestamp
WHERE
id = #{id}
</update>
実装部分の作成
実装部分としてServiceクラスにもメソッドを追加します。
insert(book: Book)
update(book: Book)
package example.service
import example.mapper.BookMapper
import example.model.Book
import org.apache.ibatis.session.SqlSessionFactory
import javax.inject.Singleton
@Singleton
class BookService(private val sqlSessionFactory: SqlSessionFactory) : BookMapper {
override fun findAll(): List<Book> {
sqlSessionFactory.openSession().use { session ->
val bookMapper = session.getMapper(BookMapper::class.java)
return bookMapper.findAll()
}
}
override fun findById(id: Int): Book {
sqlSessionFactory.openSession().use { session ->
val bookMapper = session.getMapper(BookMapper::class.java)
return bookMapper.findById(id)
}
}
override fun insert(book: Book) {
sqlSessionFactory.openSession().use { session ->
val bookMapper = session.getMapper(BookMapper::class.java)
bookMapper.insert(book)
session.commit()
}
}
override fun update(book: Book) {
sqlSessionFactory.openSession().use { session ->
val bookMapper = session.getMapper(BookMapper::class.java)
bookMapper.update(book)
session.commit()
}
}
}
Controller
データベースにアクセスするAPIを作成します。
Mapperと同様に登録と更新用の口を作成します。
-
create(book: Book)
:登録用のAPI。/book
にPOSTでリクエストすることで実行。 -
update(book: Book)
:更新用のAPI。/book
にPUTでリクエストすることで実行。
package example.controller
import example.mapper.BookMapper
import example.model.Book
import io.micronaut.http.HttpResponse
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Post
import io.micronaut.http.annotation.Put
@Controller()
class BookController(private val bookMapper: BookMapper) {
/**
* 書籍情報の全取得。
*
* @return HttpResponse
*/
@Get("/book")
fun read(): HttpResponse<List<Book>> {
return HttpResponse.ok(bookMapper.findAll())
}
/**
* 書籍情報の取得。
* ID指定
*
* @param id 書籍ID
*
* @return HttpResponse
*/
@Get("/book/{id}")
fun readById(id: Int): HttpResponse<Book> {
return HttpResponse.ok(bookMapper.findById(id))
}
/**
* 書籍情報の登録。
*/
@Post("/book")
fun create(book: Book): HttpResponse<String> {
bookMapper.insert(book)
return HttpResponse.ok()
}
/**
* 書籍情報の更新。
*/
@Put("/book")
fun update(book: Book): HttpResponse<String> {
bookMapper.update(book)
return HttpResponse.ok()
}
}
Model
モデルも若干修正します。
前回のままだとJSONが一部の項目で正しくマッピングできなかったので、JsonProperty
のアノテーションを追加します。
package example.model
import com.fasterxml.jackson.annotation.JsonProperty
import io.micronaut.core.annotation.Introspected
import java.sql.Date
/**
* 書籍データクラス
*/
@Introspected
data class Book(
@JsonProperty("id")
var id: Int?,
@JsonProperty("name")
var name: String,
@JsonProperty("publisher")
var publisher: String?,
@JsonProperty("publication_date")
var publicationDate: Date?,
)
ひとまず完成
ここまでで書籍情報の登録、更新するAPIが作成されました。
試してみましょう。
サーバを実行します。
$ cd server
$ ./gradlew run
今回、APIへのリクエストにはPOSTMANを使います。
登録できましたね!
次、更新を試してみます。
name
をtest書籍3
にしてみます。
ドン!
更新できました!
おわりに
今回追加したinsert
もupdate
もBookService
内にて各SQL実行後にsession.commit()
をしています。
参考にしたガイドでも同じように各SQLの実行後にsession.commit()
を実行しトランザクションを確定させています。
ただ、個人的にはメソッド単位でトランザクションを確定させるのは使い勝手が悪いようにも感じます。
例えば、実業務で開発するサービスでは1リクエストの中で複数のテーブル更新もすることがあるかと思います。
できればAPI単位でトランザクションを持ちたい所です。
Spring Bootだと記事が見つかるけど、Micronautだとどうやるのだろうと調査中。。
次にやりたいこと
- DB周りの自動テストを作りたい。何を使ってやるのがデファクトなんでしょうか…?
- React勉強し始めたので、フロント作ってみようかな。