この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
いまやクラウドサービスの代表格とも言えるAmazonですが、EC2やS3をはじめとして、さまざまなサービスを提供しています。 数年前に私も少しだけEC2やS3を使用したことがあるのですが、最近はあまりさわっていませんでした。 しかし今回AWSについての調査をきっかけに、各種AWSサービスについて復習&AWS SDKでの動作確認をしていきたいと思います。
今回はNoSQLの高速データベース、Amazon DynamoDBです。
今回使用した動作環境は以下のとおりです。
- OS : MacOS X 10.7.2
- Java : 1.6.0_26
- Scala : 2.9.1 final
- SBT : 0.11.2
なお、AWSへの登録は終わっているものとします。
Amazon DynamoDB
Amazon DynamoDBについての解説はここに詳しく書いてあるので詳細は省きます。 SimpleDBとかぶる部分もありますが、大量データ時のパフォーマンスや容量についてはDynamoのほうが優れているようです。 それではプログラムからDynamoDBにアクセスしてみましょう。
実行環境のセットアップ
以前と同じくsbt + scala + aws-sdk-javaをセットアップしておきましょう。
Amazon DynamoDBサンプル作成
まずは初期処理です。いつものようにキーを指定してAmazonDynamoDBClientオブジェクトを作成しています。
// アクセスキー
val accessKey = "アクセスキー"
// シークレットキー
val secretKey = "シークレットキー"
val credentials = new BasicAWSCredentials(accessKey,secretKey)
val dynamoDB = new AmazonDynamoDBClient(credentials)
次にテーブルを作成します。テーブルの作成はCreateTableRequestにテーブル名やキーの設定を行います。 ここではMyUserという名前のテーブルを、プライマリーキーはidという名前で作成しています。 createTableメソッドを呼べばテーブル作成のリクエストが送信されます。
//テーブル定義
val tableName = "MyUser"
val createTableRequest =
new CreateTableRequest()
.withTableName(tableName)
.withKeySchema(new KeySchema(new KeySchemaElement()
.withAttributeName("id")
.withAttributeType("N")))
.withProvisionedThroughput( new ProvisionedThroughput()
.withReadCapacityUnits(10L)
.withWriteCapacityUnits(10L))
val createdTableDescription = dynamoDB.createTable(createTableRequest).getTableDescription()
println("Created Table: " + createdTableDescription)
テーブルの作成ができたらテーブル情報を確認してみましょう。 describeTableメソッドを呼べば、テーブル情報を取得できます。
val describeTableRequest = new DescribeTableRequest().withTableName(tableName)
val tableDescription = dynamoDB.describeTable(describeTableRequest).getTable()
println("Table Description: " + tableDescription)
次はデータの登録です。HashMapを使用してputItemメソッドを呼べば簡単に登録することができます。
//データの登録
val item = Map("id" -> new AttributeValue().withN(Integer.toString(1)),
"name" -> new AttributeValue("taro"),
"age" -> new AttributeValue("20"))
val putItemRequest = new PutItemRequest(tableName, item)
val putItemResult = dynamoDB.putItem(putItemRequest)
println("Result: " + putItemResult)
登録ができたら次は検索です。検索はScanRequestオブジェクトにテーブル名とフィルター用マップを渡します。 フィルター用マップにはConditionオブジェクトが設定されています。 Conditionオブジェクトは、条件とその値を設定し、マップのキーにフィールド名を指定しています。
//IDが1のものを検索
val condition = new Condition()
.withComparisonOperator(ComparisonOperator.EQ.toString()) //=である
.withAttributeValueList(new AttributeValue().withN(Integer.toString(1))) //数値の1である
//検索条件のフィールドを指定
val scanFilter = Map("id" -> condition)
val scanRequest = new ScanRequest(tableName).withScanFilter(scanFilter)
val scanResult = dynamoDB.scan(scanRequest)
println("Scan Result: " + scanResult)
これで基本的な更新と検索ができるのですが、DynamoDBはDynamoDBMapperというクラスを提供しています。 これはモデルにアノテーションを付与することでORマッパーチックに使用できます。 では、MyUserモデルを作成して登録してみましょう。
@DynamoDBTable(tableName = "MyUser")
case class MyUser {
var id:Int = _
var name:String = _
def setId(_id:Int) = id = _id
def setName(_name:String) = name = _name
@DynamoDBHashKey(attributeName = "id")
def getId = id
@DynamoDBAttribute(attributeName = "name")
def getName = name
}
//DMMapperの作成
val dynamoMapper = new DynamoDBMapper(dynamoDB)
//DataMapperの登録
val user = new MyUser()
user.setId(100)
user.setName("m-user")
dynamoMapper.save(user)
DynamoDBTableアノテーションを使用してテーブルとクラスを関連付け、プロパティのgetterにもキーや属性のアノテーションを指定します。 saveメソッドにオブジェクトを渡せば、登録することができます。 ただ、このサンプルはScalaを使用しているので、モデルの記述がかなり冗長になっています。
次にDBMapperを使用した検索です。検索はloadメソッドを使用し、マップするオブジェクトとキーを渡して検索しています。
//DataMapperでidが100のものを検索
val u = dynamoMapper.load(classOf[MyUser], "100")
println("mapper id=: " + u.getId)
println("mapper name=: " + u.getName)
最後にサンプル全文をのせておきます。一度目の実行でテーブルを作成し、2度目の実行でデータ登録と検索を行います。 ※1度目の実行後、しばらく時間がたたないと、テーブルができていないのでデータ登録が失敗する可能性があります
import com.amazonaws.auth.BasicAWSCredentials
import com.amazonaws.auth.PropertiesCredentials
import com.amazonaws.services.dynamodb.AmazonDynamoDBClient
import com.amazonaws.services.dynamodb.model._
import com.amazonaws.services.dynamodb.datamodeling._
import com.amazonaws.AmazonServiceException
import scala.collection.JavaConversions._
import scala.reflect._
object DynamoMain extends App{
/**
* 指定した名前のテーブルがあるかどうか
*/
def isTable(tableName:String) :Boolean = {
try{
val request = new DescribeTableRequest().withTableName(tableName)
val tableDescription = dynamoDB.describeTable(request).getTable()
val tableStatus = tableDescription.getTableStatus()
println("tableStatus =" + tableStatus)
true
}catch {
case e:AmazonServiceException => println(e.toString);false
}
}
// アクセスキー
val accessKey = "アクセスキー"
// シークレットキー
val secretKey = "シークレットキー"
val credentials = new BasicAWSCredentials(accessKey,secretKey)
val dynamoDB = new AmazonDynamoDBClient(credentials)
val dynamoMapper = new DynamoDBMapper(dynamoDB)
//テーブル定義
val tableName = "MyUser"
if(isTable(tableName)) {
println("テーブルが存在するのでデータの登録と検索をします")
val describeTableRequest = new DescribeTableRequest().withTableName(tableName)
val tableDescription = dynamoDB.describeTable(describeTableRequest).getTable()
println("Table Description: " + tableDescription)
//通常の登録
val item = Map("id" -> new AttributeValue().withN(Integer.toString(1)),
"name" -> new AttributeValue("tarosss"),
"age" -> new AttributeValue("20"))
val putItemRequest = new PutItemRequest(tableName, item)
val putItemResult = dynamoDB.putItem(putItemRequest)
println("Result: " + putItemResult)
//DataMapperの登録
val user = new MyUser()
user.setId(100)
user.setName("m-user")
dynamoMapper.save(user)
//IDが1のものを検索
val condition = new Condition()
.withComparisonOperator(ComparisonOperator.EQ.toString())
.withAttributeValueList(new AttributeValue().withN(Integer.toString(1)))
val scanFilter = Map("id" -> condition)
val scanRequest = new ScanRequest(tableName).withScanFilter(scanFilter)
val scanResult = dynamoDB.scan(scanRequest)
println("Scan Result: " + scanResult)
//DataMapperでidが100のものを検索
val u = dynamoMapper.load(classOf[MyUser], "100")
println("mapper id=: " + u.getId)
println("mapper name=: " + u.getName)
} else {
println("テーブルがないので作成します")
val createTableRequest = new CreateTableRequest().withTableName(tableName)
.withKeySchema(new KeySchema(new KeySchemaElement().withAttributeName("id").withAttributeType("N")))
.withProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(10L).withWriteCapacityUnits(10L))
val createdTableDescription = dynamoDB.createTable(createTableRequest).getTableDescription()
println("Created Table: " + createdTableDescription)
}
}
@DynamoDBTable(tableName = "MyUser")
case class MyUser {
var id:Int = _
var name:String = _
def setId(_id:Int) = id = _id
def setName(_name:String) = name = _name
@DynamoDBHashKey(attributeName = "id")
def getId = id
@DynamoDBAttribute(attributeName = "name")
def getName = name
}
実行するにはいつものようにsbtを起動してrunコマンドを実行してください。
% sbt
> run
まとめ
今回はDynamoDBを使用してみました。 まだベータですが、simpelDBに代わる高速なデータベースということで、今後も使用する機会は多いと思います。
参考サイトなど
- AWS SDK for Java javadoc: http://docs.amazonwebservices.com/AWSJavaSDK/latest/javadoc/
- Amazon DynamoDB公式ドキュメントリンク: http://aws.amazon.com/jp/documentation/dynamodb/