#author("2018-09-09T09:09:29+00:00","","")
[[AWSメモ]] >
* AWS Java SDKでDynamoDBのCRUDを書いてみる [#q4c74446]
#setlinebreak(on);

#contents
-- 関連
--- [[AWS Kinesis Client Libraryでコンシューマ開発]]
-- 参考
--- [[AWS SDK for Java 開発者ガイド>https://docs.aws.amazon.com/ja_jp/sdk-for-java/v1/developer-guide/welcome.html]] > [[AWS SDK for Java コード例>https://docs.aws.amazon.com/ja_jp/sdk-for-java/v1/developer-guide/prog-services.html]] > [[AWS SDK for Java を使用した DynamoDB の例>https://docs.aws.amazon.com/ja_jp/sdk-for-java/v1/developer-guide/examples-dynamodb.html]] > [[DynamoDB での項目の操作>https://docs.aws.amazon.com/ja_jp/sdk-for-java/v1/developer-guide/examples-dynamodb-items.html]]
--- [[AWS SDK for Java APIリファレンス>https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/index.html]]

* AWS Java SDK の DynamoDB関連パッケージ [#h8aa9d32]
** AWS Java SDK の DynamoDB関連パッケージ [#h8aa9d32]
#html(<div style="padding-left:10px;">)

以下の com.amazonaws.services.dynamodbv2 が DynamoDB関連パッケージ。
https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/index.html

#html(</div>)

** Java SDK利用時の注意点 [#aa82bce4]
#html(<div style="padding-left:10px;">)

例えば、同じデータ登録を行う場合でも、様々なインターフェースが用意されている為、要件に応じて使い分ける必要がある。
例として、レコードを登録するインターフェースを挙げるが、ざっと見渡しただけで、これだけのインターフェースがある。

com.amazonaws.services.dynamodbv2.AmazonDynamoDB
|戻り値|メソッド|h
|PutItemResult|putItem(PutItemRequest request)|
|PutItemResult|putItem(String tableName, Map<String,AttributeValue> item)|
|PutItemResult|putItem(String tableName, Map<String,AttributeValue> item, String returnValues)|

com.amazonaws.services.dynamodbv2.document.Table
|戻り値|メソッド|h
|PutItemOutcome|putItem(Item item)|
|PutItemOutcome|putItem(Item item, Expected... expected)|
|PutItemOutcome|putItem(Item item, String conditionExpression, Map<String,String> nameMap, Map<String,Object> valueMap)|
|PutItemOutcome|putItem(PutItemSpec spec)|
※さらにバッチWrite 等は com.amazonaws.services.dynamodbv2.document.DynamoDB で提供されている。

#html(<div style="padding:5px;background:#eeffee;display:inline-block;border-radius:5px;border:1px solid #333;box-shadow:1px 1px 10px #ccc">)
個人的には、

AmazonDynamoDB.putItem(PutItemRequest request)、AmazonDynamoDB.updateItem(UpdateItemRequest request) 等の XxxxItemRequest 系を引数にとるメソッドか、

Table.putItem(PutItemSpec spec)、Table.updateItem(UpdateItemSpec spec) 等の XxxxItemSpec 系を引数にとるメソッドを使用しておけば大抵のケースには対応できる気はしている。

逆に、putItem(Item item) などを使用していると、登録結果のデータを戻り値で取得したいケースや、消費したキャパシティユニットを戻り値で得たいケースには対応できない。

※XxxxItemRequest とか XxxxItemSpec だと withReturnValues や withReturnConsumedCapacity で登録/更新結果や、キャパシティユニットも返却できるようになるので。
#html(</div>)

#html(</div>)

** DynamoDBMapperについて [#da7679f4]
#html(<div style="padding-left:10px;">)

Java版のSDKでは、DynamoDBMapper といういわゆるO/Rマッピングツールが提供されている。
''&color(red){ただし、なぜかデフォルトでMap型がサポートされていない。};''
※恐らく DynamoDBTypeConverted アノテーションを利用して、自力でコンバーターを作ればマッピング可能と思われる。

|タイトル|参考URL|h
|Java: DynamoDBMapper|https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBMapper.html|
|サポートされているデータの種類|https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBMapper.DataTypes.html|
|任意データのマッピング|https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBMapper.ArbitraryDataMapping.html|

#html(</div>)

** テスト用プロジェクトの作成 [#md68e52a]
#html(<div style="padding-left:10px;">)

https://docs.aws.amazon.com/ja_jp/sdk-for-java/v1/developer-guide/setup-project-maven.html

#myterm2(){{
mkdir aws-sdk-java-test && cd aws-sdk-java-test
gradle init --type java-application
rm -rf src/main/java/*
rm -rf src/test/java/*
}}

build.gradle の編集
#mycode2(){{
plugins {
    id 'java'
    id 'application'
}

mainClassName = 'ScanTable'

dependencies {
    compile 'com.google.guava:guava:23.0'
    //compile 'com.amazonaws:aws-java-sdk:latest.release'
    compile 'com.amazonaws:aws-java-sdk-dynamodb:latest.release'
    compile 'com.google.code.gson:gson:2.8.2'
    compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
    compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'

    testCompile 'junit:junit:4.12'
}

repositories {
    jcenter()
}

run {
    // 引数で指定されたクラスを実行する
    if (project.hasProperty('main')) {    
        main(project.main)
    }   
}
}}

#html(</div>)

** テーブルの作成 [#we0981db]
#html(<div style="padding-left:10px;">)

*** DDL格納用フォルダ作成 [#ra9f949b]
#html(<div style="padding-left:10px;">)
#myterm2(){{
mkdir -p  src/test/resources/ddl
}}
#html(</div>)

*** テーブル定義の作成 [#pb4ab3e3]
#html(<div style="padding-left:10px;">)

src/test/resources/ddl/SampleTable.json
#mycode2(){{
{
    "TableName": "SampleTable",
    "AttributeDefinitions": [
        { "AttributeName": "pkey", "AttributeType": "S" },
        { "AttributeName": "skey", "AttributeType": "S" }
    ],  
    "KeySchema": [
        { "AttributeName": "pkey", "KeyType": "HASH" },
        { "AttributeName": "skey", "KeyType": "RANGE" }
    ],  
    "ProvisionedThroughput": { "WriteCapacityUnits": 1, "ReadCapacityUnits": 1 } 
}
}}
#html(</div>)

*** テーブル作成 [#jf603ce0]
#html(<div style="padding-left:10px;">)
#myterm2(){{
aws dynamodb create-table --cli-input-json file://src/test/resources/ddl/SampleTable.json
}}
#html(</div>)

#html(</div>)

** テストデータの登録 [#d6c65409]
#html(<div style="padding-left:10px;">)

#myterm2(){{
aws dynamodb put-item --table-name SampleTable \
  --item '{"pkey": {"S": "test0"}, "skey": {"S": "2018-01-01T00:00:00.001"}, "stringData": {"S": "testdata-001"} }'
aws dynamodb put-item --table-name SampleTable \
  --item '{"pkey": {"S": "test0"}, "skey": {"S": "2018-01-01T00:00:00.002"}, "stringData": {"S": "testdata-002"} }'
aws dynamodb put-item --table-name SampleTable \
  --item '{"pkey": {"S": "test0"}, "skey": {"S": "2018-01-01T00:00:00.003"}, "stringData": {"S": "testdata-003"} }'
aws dynamodb put-item --table-name SampleTable \
  --item '{"pkey": {"S": "test0"}, "skey": {"S": "2018-01-01T00:00:00.004"}, "stringData": {"S": "testdata-004"} }'
aws dynamodb put-item --table-name SampleTable \
  --item '{"pkey": {"S": "test0"}, "skey": {"S": "2018-01-01T00:00:00.005"}, "stringData": {"S": "testdata-005"} }'
}}
#html(</div>)

&br;


** 処理(CRUD)の作成 [#a3649cf0]
#html(<div style="padding-left:10px;">)

*** 検索処理(scan) [#r4f3d8c9]
#html(<div style="padding-left:10px;">)
src/main/java/ScanTable.java
#mycode2(){{
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; 
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.ScanRequest;
import com.amazonaws.services.dynamodbv2.model.ScanResult;

import java.util.List;
import java.util.Map;

public class ScanTable {

    private static AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.standard().build();

    public static void main(String[] args){

        ScanRequest request = new ScanRequest("SampleTable");
        ScanResult result = amazonDynamoDB.scan(request);
        Integer count = result.getCount();
        System.out.println("#########################################");
        System.out.println("count: " + count );
        List<Map<String,AttributeValue>> items = result.getItems();
        for (Map<String,AttributeValue> item : items) {
            System.out.println(item);
        }   
        System.out.println("#########################################");
    }   

}
}}

テスト実行
#myterm2(){{
gradle run -Pmain=ScanTable
}}
&br;

#html(</div>)


*** 登録処理 [#a3fbbdb1]
#html(<div style="padding-left:10px;">)

src/main/java/PutItem.java
#mycode2(){{
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; 
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
import com.amazonaws.services.dynamodbv2.model.PutItemResult;

import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class PutItem {

    private static AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.standard().build();

    public static void main(String[] args){

        String datetime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS"));

        String pkey = "test1";
        String skey = datetime;
        String stringData = "testdata-" + UUID.randomUUID().toString();

        Map<String,AttributeValue> item = new HashMap<String,AttributeValue>();
        item.put("pkey"       ,   new AttributeValue().withS(pkey));
        item.put("skey"       ,   new AttributeValue().withS(skey));
        item.put("stringData" ,   new AttributeValue().withS(stringData));

        PutItemResult result = amazonDynamoDB.putItem(new PutItemRequest("SampleTable", item));

        System.out.println("#########################################");
        System.out.println(result);
        System.out.println("#########################################");
    }   
}
}}

テスト実行
#myterm2(){{
gradle run -Pmain=PutItem
}}
&br;

#html(</div>)


*** 検索処理(query) [#le0b378d]
#html(<div style="padding-left:10px;">)

src/main/java/QueryTable.java
#mycode2(){{
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; 
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
import com.amazonaws.services.dynamodbv2.model.Condition;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.QueryRequest;
import com.amazonaws.services.dynamodbv2.model.QueryResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class QueryTable {

    private static AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.standard().build();

    public static void main(String[] args){

        QueryRequest request = new QueryRequest("SampleTable");

        Map<String,Condition> keyConditions = new HashMap<String,Condition>();
        keyConditions.put("pkey", new Condition().withComparisonOperator(ComparisonOperator.EQ).withAttributeValueList(new AttributeValue("test1")));
        request.setKeyConditions(keyConditions);
        
        // KeyConditionExpression, ExpressionAttributeNames, ExpressionAttributeValues を使用する場合
        /*
        request.setKeyConditionExpression("#pkey = :pkey");
        request.setExpressionAttributeNames(new HashMap<String,String>(){
            {put("#pkey", "pkey");}
        });
        request.setExpressionAttributeValues(new HashMap<String,AttributeValue>(){
            {put(":pkey", new AttributeValue("test1"));}
        });
        */

        QueryResult result = amazonDynamoDB.query(request);
        Integer count = result.getCount();
        System.out.println("#########################################");
        System.out.println("count: " + count );
        List<Map<String,AttributeValue>> items = result.getItems();
        for (Map<String,AttributeValue> item : items) {
            System.out.println(item);
        }   
        System.out.println("#########################################");
    }   
}
}}

テスト実行
#myterm2(){{
gradle run -Pmain=QueryTable
}}
&br;

#html(</div>)


*** 更新処理(update) [#m3a4985c]
#html(<div style="padding-left:10px;">)

src/main/java/UpdateItem.java
#mycode2(){{
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; 
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest;
import com.amazonaws.services.dynamodbv2.model.UpdateItemResult;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class UpdateItem {

    private static AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.standard().build();

    public static void main(String[] args){

        Map<String, String> attributeNames = new HashMap<String,String>(){{
            put("#stringData", "stringData");
        } }; 

        Map<String, AttributeValue> attributeValues = new HashMap<String, AttributeValue>(){{
            put(":stringData", new AttributeValue().withS("testdata-" + UUID.randomUUID().toString()));
        } }; 

        Map<String, AttributeValue> key = new HashMap<String, AttributeValue>();
        key.put("pkey", new AttributeValue().withS("test0"));
        key.put("skey", new AttributeValue().withS("2018-01-01T00:00:00.001"));

        UpdateItemRequest request = new UpdateItemRequest()
            .withTableName("SampleTable")
            .withKey(key)
            //.withConditionExpression("#other = :other")   // 条件付き更新を行う場合
            .withUpdateExpression("set #stringData = :stringData")
            .withExpressionAttributeNames(attributeNames)
            .withExpressionAttributeValues(attributeValues);

        UpdateItemResult result = amazonDynamoDB.updateItem(request);

        System.out.println("#########################################");
        System.out.println(result);
        System.out.println("#########################################");
    }   
}
}}

テスト実行
#myterm2(){{
gradle run -Pmain=UpdateItem
}}
&br;

#html(</div>)


*** 削除処理(delete) [#oda04e6e]
#html(<div style="padding-left:10px;">)

src/main/java/DeleteItem.java
#mycode2(){{
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; 
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest;
import com.amazonaws.services.dynamodbv2.model.DeleteItemResult;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class DeleteItem {

    private static AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.standard().build();

    public static void main(String[] args){

        Map<String, AttributeValue> key = new HashMap<String, AttributeValue>();
        key.put("pkey", new AttributeValue().withS("test0"));
        key.put("skey", new AttributeValue().withS("2018-01-01T00:00:00.001"));

        DeleteItemRequest request = new DeleteItemRequest()
            .withTableName("SampleTable")
            .withKey(key);

        DeleteItemResult result = amazonDynamoDB.deleteItem(request);

        System.out.println("#########################################");
        System.out.println(result);
        System.out.println("#########################################");
    }   
}
}}

テスト実行
#myterm2(){{
gradle run -Pmain=DeleteItem
}}
&br;

#html(</div>)

#html(</div>)


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS