LoginSignup
4
6

More than 5 years have passed since last update.

ScalaからDynamoDBを扱うサンプル ~DynamoDBMapper編~

Last updated at Posted at 2016-09-02

前回の続きです。今回はより高級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
  )
4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6