サーバサイドKotlinでKomapperを使ってみよう
Kotlin製ORマッパーのKomapperが5月に1.0のリリースされると聞いてSpringBoot上で試してみました。といってもサンプルソースも豊富なのでそれに毛が生えた程度です。。
CRUD
H2を利用してperson
テーブルを用意してCRUDを動かします。
create table person ( id int GENERATED ALWAYS AS IDENTITY (START WITH 3) PRIMARY KEY, name varchar(255) not null, email varchar(255) not null );
エンティティ、マッピング定義
テーブルと対になるエンティティとPKなどの項目にアノテーションを定義して項目マッピングを行います。
data class Person( val id: Int? = null, val name: String, val email: String, ) @KomapperEntityDef(Person::class) data class PersonDef( @KomapperId @KomapperAutoIncrement val id: Nothing, )
クエリ実行
QueryDSLを利用してSQLを実行します。メソッドチェーンでやりたいことをつなげていく感じなので直感的でわかりやすいですね。
@Service class PersonService( private val database: JdbcDatabase ) { fun findAll(): List<Person> { return database.runQuery { val p = Meta.person QueryDsl.from(p).orderBy(p.id) } } fun findById(id: Int): Person { return database.runQuery { val p = Meta.person QueryDsl.from(p).where{ p.id eq id }.first() } } @Transactional fun insert(name: String, email: String): Person { return database.runQuery { val person = Person(name = name, email = email) val p = Meta.person QueryDsl.insert(p).single(person) } } @Transactional fun update(id: Int, name: String, email: String): Person { return database.runQuery { val person = findById(id) val p = Meta.person QueryDsl.update(p).single(person.copy(name = name, email = email)) } } @Transactional fun delete(id: Int) { database.runQuery { val person = findById(id) val p = Meta.person QueryDsl.delete(p).single(person) } } }
結合
テーブル結合をしたSQLも試してみます。チームテーブル、チームメンバーテーブルを用意して、personテーブルを含めた3テーブルの結合を行います。
DDLは次の通りです。
create table team ( id int GENERATED ALWAYS AS IDENTITY (START WITH 3) PRIMARY KEY, name varchar(255) not null ); create table team_member ( id int GENERATED ALWAYS AS IDENTITY (START WITH 4) PRIMARY KEY, team_id int not null, member_id int not null );
エンティティとマッピング
data class Team( val id: Int? = null, val name: String, ) @KomapperEntityDef(Team::class) data class TeamDef( @KomapperId @KomapperAutoIncrement val id: Nothing, ) data class TeamMember( val id: Int? = null, val teamId: Int, val memberId: Int, ) @KomapperEntityDef(TeamMember::class) data class TeamMemberDef( @KomapperId @KomapperAutoIncrement val id: Nothing, )
left joinを行います。leftJoinメソッドで結合するテーブルを指定し、selectメソッドで取得するカラムを指定します。
@Service class TeamService( private val database: JdbcDatabase ) { fun findById(id: Int): TeamDTO? { val g = Meta.team val m = Meta.teamMember val p = Meta.person val result = database.runQuery { QueryDsl.from(g) .leftJoin(m) { g.id eq m.teamId } .leftJoin(p) { m.memberId eq p.id } .where { g.id eq id } .orderBy(m.id) .select(g.id, g.name, p.id, p.name, p.email) } val members = result.map { Member(it[p.id]!!, it[p.name]!!, it[p.email]!!) } val member = result.first() val id = member[g.id] val name = member[g.name] return TeamDTO(id!!, name!!, members) } }
完成したソースは↓です。
感想
SQLをメソッドチェーンで組み立てていくことができるので悩まずに実装できるのがいいですね。アノテーションプロセッサーを利用しているのでMeta
で指定できるテーブルはコンパイルをしていないと怒られてしまうのは注意が必要です。(大した問題ではないが)
R2DBCに対応しているORマッパーは少ないので採用候補に上がるプロジェクトも多いんじゃないでしょうか。
基本機能を試した程度ですが、これから採用実績は増えていくこと間違いなしだと思います!!