前回の続きです。今回はより高級APIであるDynamoDBMapperを扱います。
こちらも足りないAPIは必要に応じて増やしていきます。
フルのソースコードはこちら
https://github.com/Peranikov/dynamodb-scala-sample/blob/master/src/main/scala/HighLevelApiSample.scala
はじめに
build.sbtやテーブル作成などは前回のはじめにをご覧ください。
この辺りをimportしておきます。
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient
import com.amazonaws.services.dynamodbv2.datamodeling.{DynamoDBQueryExpression, _}
import com.amazonaws.services.dynamodbv2.model.AttributeValue
アイテムをマッピングするクラスを定義
DynamoDBのアイテムをマッピングするためのクラスを定義します。
各Annotationの解説はこちらです。
getter/setterのメソッド名はgetXXX/setXXXにする必要があります。
@DynamoDBTable(tableName="Music")
class MusicItem {
private var artist: String = _
private var songTitle: String = _
private var members: java.util.Set[String] = _
private var origin: String = _
// Hash key
@DynamoDBHashKey(attributeName="Artist")
@DynamoDBIndexRangeKey(attributeName="Artist", globalSecondaryIndexName="OriginGlobalIndex")
def getArtist: String = { this.artist }
def setArtist(artist: String): Unit = { this.artist = artist }
// Range key
@DynamoDBRangeKey(attributeName="SongTitle")
def getSongTitle: String = { this.songTitle }
def setSongTitle(songTitle: String): Unit = { this.songTitle = songTitle }
@DynamoDBIndexHashKey(attributeName="Origin", globalSecondaryIndexName="OriginGlobalIndex")
@DynamoDBIndexRangeKey(attributeName="Origin", localSecondaryIndexName="OriginIndex", globalSecondaryIndexName="OriginGlobalIndex")
def getOrigin: String = { this.origin }
def setOrigin(origin: String): Unit = { this.origin = origin }
@DynamoDBAttribute(attributeName="Members")
def getMembers: java.util.Set[String] = { this.members }
def setMembers(members: java.util.Set[String]): Unit = { this.members = members }
override def toString: String = {
s"""{
| Artist: ${this.artist}
| SongTitle: ${this.songTitle}
| Origin: ${this.origin}
| Members: ${this.members}
|}""".stripMargin
}
}
Scalaらしくコンパニオンオブジェクトを定義しておくと便利です。
object MusicItem {
def apply(artist: String, songTitle: String): MusicItem = {
val item = new MusicItem()
item.setArtist(artist)
item.setSongTitle(songTitle)
item
}
def apply(artist: String, songTitle: String, members: Set[String]): MusicItem = {
val item = new MusicItem()
item.setArtist(artist)
item.setSongTitle(songTitle)
item.setMembers(members.asJava)
item
}
}
クライアントとマッパーの生成
Javaでのサンプルはこちらです。
val client: AmazonDynamoDBClient = (new AmazonDynamoDBClient())
.withEndpoint("http://localhost:8000")
val mapper: DynamoDBMapper = new DynamoDBMapper(client)
Save
アイテムを保存します。APIで言うPutにあたります。
println(
mapper.save(MusicItem("Guns N' Roses", "Welcome to the Jungle", Set("Axl Rose", "Slash")))
)
Load
アイテムを取得します。APIで言うGetにあたります。
println(
mapper.load(classOf[MusicItem], "Guns N' Roses", "Welcome to the Jungle")
)
Delete
アイテムを削除します。
println(
mapper.delete(MusicItem("Guns N' Roses", "Welcome to the Jungle"))
)
Batch Save
複数のアイテムを同時に保存します。
println(
mapper.batchSave(Seq(
MusicItem("Black Sabbath", "Paranoid", Set("Ozzy Osbourne", "Tony Iommi")),
MusicItem("Black Sabbath", "Mr. Crowley", Set("Ozzy Osbourne", "Tony Iommi")),
MusicItem("Black Sabbath", "Heaven or Hell", Set("Ronnie James Dio", "Tony Iommi"))
).asJava)
)
Query
Queryを発行します。ここではHashキーを指定しています。
println(
mapper.query(
classOf[MusicItem],
new DynamoDBQueryExpression[MusicItem]()
.withKeyConditionExpression("Artist = :a")
.withExpressionAttributeValues(Map(
":a" -> new AttributeValue("Black Sabbath")
).asJava)
).asScala
)
リミットを指定してQueryPage
QueryPageは最初のページ、1MBに収まる分だけを取得します。
なおリミットはQueryPageでのみ有効で、Queryに指定しても無視されます。
val limitQueryResult = mapper.queryPage(
classOf[MusicItem],
new DynamoDBQueryExpression[MusicItem]()
.withKeyConditionExpression("Artist = :a")
.withExpressionAttributeValues(Map(
":a" -> new AttributeValue("Black Sabbath")
).asJava)
.withLimit(1)
)
続きのページをクエリ
QueryPageで取得後に続きのページがある場合、LastEvaluatedKeyが取得できるのでこれを指定して続きを取得します。
println(s"LastEvaluatedKey: ${limitQueryResult.getLastEvaluatedKey}")
println(
mapper.queryPage(
classOf[MusicItem],
new DynamoDBQueryExpression[MusicItem]()
.withKeyConditionExpression("Artist = :a")
.withExpressionAttributeValues(Map(
":a" -> new AttributeValue("Black Sabbath")
).asJava)
.withExclusiveStartKey(limitQueryResult.getLastEvaluatedKey)
).getResults.asScala
)
Local Secondly IndexからQuery
Local Secondly Index名を指定して取得します。
ここではインデックス作成時にProjectionをKEYS_ONLYと指定しているので、Key以外であるMembersは取得されません。
println(
mapper.query(
classOf[MusicItem],
new DynamoDBQueryExpression[MusicItem]()
.withIndexName("OriginIndex")
.withKeyConditionExpression("Artist = :a and Origin = :o")
.withExpressionAttributeValues(Map(
":a" -> new AttributeValue("Black Sabbath"),
":o" -> new AttributeValue("England")
).asJava)
).asScala
)
Global Secondly IndexからQuery
Global Secondly Index(GSI)名を指定して取得します。
このGSIではOriginをプライマリーキーに指定しているのでOriginに対してクエリが実行できます。
ProjectionExpressionにOrigin, Artistを指定しているので、それらしか取得しません。
println(
mapper.query(
classOf[MusicItem],
new DynamoDBQueryExpression[MusicItem]()
.withIndexName("OriginGlobalIndex")
.withKeyConditionExpression("Origin = :o")
.withExpressionAttributeValues(Map(
":o" -> new AttributeValue("England")
).asJava)
.withProjectionExpression("Origin, Artist")
.withScanIndexForward(false)
.withConsistentRead(false)
).asScala
)
Scan
全てのアイテムを取得します。
println(
mapper.scan(classOf[MusicItem], new DynamoDBScanExpression()).asScala
)