まず、Spring Data RestでサクッとRestできるようにします
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なんかもルーティングされてるとかステキですね
いじょ