DB
適当クエリでテーブル2つとデータを作成create table memo_title ( id serial primary key, title varchar(255) not null, comment varchar(255) ); create table memo_value ( id serial primary key, value varchar(255) not null, title_id int not null ); insert into memo_title(id, title, comment) values(1, 'TESTタイトル', 'メモのコメントですよ'); insert into memo_value(id, value, title_id) values(1, 'ほげ', 1); insert into memo_value(id, value, title_id) values(2, 'ふが', 1);
memo_value が memo_title の id を持ってて親子関係がある感じです。
(面倒なのでFKとか無しで)
続いてAPのコード
MemoTitle.kt
@Entity @Table(name="memo_title") class MemoTitle { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0 @Column(nullable = false) lateinit var title: String @Column lateinit var comment: String } @Repository interface MemoTitleRepository: PagingAndSortingRepository<MemoTitle, Long>
MemoValue.kt
@Entity @Table(name="memo_value") class MemoValue { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0 @Column(nullable = false) lateinit var value: String @Column(nullable = false) var title_id: Long = 0 } @Repository interface MemoValueRepository : PagingAndSortingRepository<MemoValue, Long>
こういう時に kotlin だと関連したコードが1ファイルに書けるってだけでウレシイ。。。
この時点で実行すると
MemoTitle
curl http://localhost:9000/api/memoTitles { "_embedded" : { "memoTitles" : [ { "title" : "TESTタイトル", "comment": "メモのコメントですよ", "_links" : { "self" : { "href" : "http://localhost:9000/api/memoTitles/1" }, "memoTitle" : { "href" : "http://localhost:9000/api/memoTitles/1" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoTitles" }, "profile" : { "href" : "http://localhost:9000/api/profile/memoTitles" } }, "page" : { "size" : 20, "totalElements" : 1, "totalPages" : 1, "number" : 0 } }
MemoValue
curl http://localhost:9000/api/memoValues { "_embedded" : { "memoValues" : [ { "id" : 1, "value" : "ほげ", "title_id" : 1, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues/1" }, "memoValue" : { "href" : "http://localhost:9000/api/memoValues/1" }, "title" : { "href" : "http://localhost:9000/api/memoValues/1/title" } } }, { "id" : 2, "value" : "ふが", "title_id" : 1, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues/2" }, "memoValue" : { "href" : "http://localhost:9000/api/memoValues/2" }, "title" : { "href" : "http://localhost:9000/api/memoValues/2/title" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues" }, "profile" : { "href" : "http://localhost:9000/api/profile/memoValues" } }, "page" : { "size" : 20, "totalElements" : 2, "totalPages" : 1, "number" : 0 } }
MemoValueをGETしたときにtitleも欲しかったりしたとします
MemoValueにMemoTitleとのアソシエーションを追加し、Projectionを指定します
MemoValue.kt
@Entity @Table(name="memo_value") class MemoValue { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0 @Column(nullable = false) lateinit var value: String @Column(nullable = false) var title_id: Long = 0 @ManyToOne(targetEntity = MemoTitle::class, fetch = FetchType.EAGER) @JoinColumn(name = "title_id", insertable = false, updatable = false, nullable = false) lateinit var title: MemoTitle } @Projection(types = arrayOf(MemoValue::class)) interface MemoValueProjection { var id: Long var value: String var title: MemoTitle } @RepositoryRestResource(excerptProjection = MemoValueProjection::class) interface MemoValueRepository : PagingAndSortingRepository<MemoValue, Long>
excerptProjectionとするとリポジトリの戻りが全てそのprojectionになります
ここで指定せずに、GETリクエストに?projection=Projection名とか指定することもできます
これで実行すると
MemoValue
curl http://localhost:9000/api/memoValues { "_embedded" : { "memoValues" : [ { "id" : 1, "value" : "ほげ", "title" : { "title" : "TESTタイトル", "comment": "メモのコメントですよ" }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues/1" }, "memoValue" : { "href" : "http://localhost:9000/api/memoValues/1{?projection}", "templated" : true }, "title" : { "href" : "http://localhost:9000/api/memoValues/1/title" } } }, { "id" : 2, "value" : "ふが", "title" : { "title" : "TESTタイトル", "comment": "メモのコメントですよ" }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues/2" }, "memoValue" : { "href" : "http://localhost:9000/api/memoValues/2{?projection}", "templated" : true }, "title" : { "href" : "http://localhost:9000/api/memoValues/2/title" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues" }, "profile" : { "href" : "http://localhost:9000/api/profile/memoValues" } }, "page" : { "size" : 20, "totalElements" : 2, "totalPages" : 1, "number" : 0 } }
無事にアソシエーション先の情報も取れました
続いて、title.commentは要らないんすよ、とかなったとします
その場合はMemoTitleもProjectionを指定します
MemoTitle.kt
@Projection(types = arrayOf(MemoTitle::class)) interface MemoTitleProjection { var title: MemoTitle }
MemoValue.kt
@Projection(types = arrayOf(MemoValue::class)) interface MemoValueProjection { var id: Long var value: String var title: MemoTitleProjection }
これで実行すると
MemoValue
curl http://localhost:9000/api/memoValues { "_embedded" : { "memoValues" : [ { "id" : 1, "value" : "ほげ", "title" : { "title" : "TESTタイトル" }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues/1" }, "memoValue" : { "href" : "http://localhost:9000/api/memoValues/1{?projection}", "templated" : true }, "title" : { "href" : "http://localhost:9000/api/memoValues/1/title" } } }, { "id" : 2, "value" : "ふが", "title" : { "title" : "TESTタイトル" }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues/2" }, "memoValue" : { "href" : "http://localhost:9000/api/memoValues/2{?projection}", "templated" : true }, "title" : { "href" : "http://localhost:9000/api/memoValues/2/title" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:9000/api/memoValues" }, "profile" : { "href" : "http://localhost:9000/api/profile/memoValues" } }, "page" : { "size" : 20, "totalElements" : 2, "totalPages" : 1, "number" : 0 } }
title.commentが削られています
これだけの記述で必要な情報のGETに加えてPOSTやらPUTやらPATCHでのINSERTやDELETEやUPDATEなんかもルーティングされてるとかステキですね
いじょ