AWS SDK for Javaのバージョンは、1.3.10です。
使ってみた時に詰まった点をメモってみました。
1. ResourceNotFoundExceptionが出る原因
以下のような例外が発生する場合の原因と対処法です。
[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException, AWS Error Message: Requested resource not found
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException,AWS Error Message: Requested resource not found
原因1 Endpointが設定されていない
val client : AmazonDynamoDBClient = ...// init
client.setEndpont("http://dynamodb.ap-northeast-1.amazonaws.com"); // <- 東京RegionのEndpoint設定。
自分のDynamoDBを使用しているRegionを設定しましょう。Endpoingの一覧は
こちら
原因2 Tableが作成されていない
指定したTableが存在しない場合にも同じ例外が発生します。Tableが正常に作成されているかと、プログラム中でTable名の指定が間違ってないかを確認しましょう。
Tableがまだ作成されていない場合は、ManagedConsoleから作成してあげましょう。
2. ValidationExceptionが出る原因
以下のような例外が発生する場合の原因と対処法です。
[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema
原因1 HashKeyまたはRangeKeyの型が違う
HashKeyとRangeKeyの型が間違っている場合に良く起こります。
Table定義を確認して、StringとNumber型の指定が正しいことを確認してください。
new AttributeValue().withS("hogehoge") //Table定義ではNumberになっている場合コレが原因になる
3. O/R mapperを使ってRangeKeyにDate型を指定したけど、queryで上手くフィルターできない
O/R mapperでDateを使用する場合は、DynamoDB上ではISO8601の日付フォーマットの文字列として保存されます。
import com.amazonaws.util.DateUtils
DateUtils dateUtils = new DateUtils();
dateUtils.formatIso8601Date(date)
dateUtils.parseIso8601Date(dateString)
で、変換できます。queryやscan時のConditionで指定する場合は、このメソッドを使いましょう。
4. ScalaからAWS SDKのO/R mapperを使う
Java Beansにしか対応していないので、通常のScalaPlainObjectでは上手く認識してくれません。
基本的には、@BeansInfoアノテーションをつければ、getter/setterを生成してくれるので変数の定義に関しては問題ありません。
しかし、varにつけたアノテーションは、コンパイルされるタイミングで、同名のprivate fieldのほうに付けれられてしまいます。
そのため、AWS SDKのアノテーションが有効にならないので、地道に手作業でgetter/setterを作成してアノテーションをつけないと行けません。
また、case classにしておくと勝手にequalsやhashCode関数をオーバーライドしてくれるので便利ですが、引数なしのコンストラクタもあわせて定義しておいてあげましょう。
NG
import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._
case class Hoge( @BeansInfo @DynamoDBHashKey(attributeName="Id") var id : Long,
@BeansInfo var nickname : String){
def this() = this(0,"")
}
OK
import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._
case class Hoge(var id : Long,
@BeansInfo var nickname : String){
def this() = this(0,"")
@DynamoDBHashKey(attributeName="Id")
def getId() : Long = id
def setId(id : Long) = this.id = id;
}
5. O/R Mapper時にクラスの継承を行うと変数が上手くマップされない
幾つかのテーブルが同じようなテーブルで同じ変数持ってるから、継承で共通化しちゃえと思って継承を使ってみたところ、上手くO/R Mapperがフィールドをマップしてくれませんでした。
回避方法は、「継承元のクラスにDynamoDBTableアノテーションを付ける」になります。
NG
class Hoge(... , var hoge : String){
def this() = this(...)
...
def getHoge():String = hoge
def setHoge( hoge : String){ this.hoge = hoge}
}
@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge
OK
@DynamoDBTable(tableName="DummyAnnotation") //<- これ重要
class Hoge ...
@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge
ちなみに、こうなっちゃう原因はDynamoDBRefrector@isRelevantGetter(DynamoDBRefrector.java:92)の
private boolean isRelevantGetter(Method m) {
return (m.getName().startsWith("get") || m.getName().startsWith("is")) && m.getParameterTypes().length == 0
&& m.getDeclaringClass().getAnnotation(DynamoDBTable.class) != null
&& !m.isAnnotationPresent(DynamoDBIgnore.class);
}
で、メソッドのDeclaringClassのアノテーションを取ってしまっちゃってるからです。