AWS SDK v2のDynamoDBでupdateを呼ぶとNumber要素がインクリメントするようにしたい

はじめに

DynamoDBで特定の項目のNumber要素をインクリメント(デクリメント)したくなった。Get→インクリメント→Updateはナンセンスなので、もっと簡単にやる方法を探した。

対処

@DynamoDbAtomicCounterアノテーションを利用すると解決した。

@DynamoDbBean
public class Item {

private String id;

private int type;

private Long value;

@DynamoDbPartitionKey
@DynamoDbAttribute(value = "ITEM_ID")
public String getId() {
return id;
}

@DynamoDbSortKey
@DynamoDbAttribute(value = "TYPE")
public int getType() {
return type;
}

@DynamoDbAtomicCounter(startValue = 100, delta = 50)
@DynamoDbAttribute(value = "VALUE")
public Long getValue() {
return value;
}
}

これを利用する時の注意点としては、DynamoDbAtomicCounterを利用したい変数はLong型で宣言する必要がある。(おそらく存在しない=nullを取る必要があるので、オブジェクト型なのだと推測する。)

あとは、DynamoDbEnhancedClientから作成したDynamoDbTableに対してupdate要求を行う。

Item i = new Item();
i.setId("id");
i.setType(1);

Item result = table.updateItem(item);

これを呼び出すと、idとtypeに合致する項目が存在しなかった場合、startValueで指定した値でupdateされ、resultにはvalue=100のitemが取得される。

逆にidとtypeに合致する項目が存在している場合は、現在のvalueに対してdeltaを加算した値でupdateされ、resultにはvalue=現在の値+deltaのitemが取得される。

注意

この方法を取ると、任意の値でvalueをupdateすることができない。さらに任意のdeltaでインクリメント(デクリメント)することもできなくなる。

ItemクラスのsetValue()を呼び出して、任意の値を設定した上でupdateを呼び出すとDynamoDbExceptionが発生する。

Invalid UpdateExpression: Two document paths overlap with each other; must remove or rewrite one of these paths

putすれば置き換わると考えたが、putは例外こそ出ないが、startValueのItemに置き換えられてしまう。

 

任意の値でvalueをupdateしたり、任意のdeltaでインクリメント(デクリメント)する場合はStaticTableSchemaを利用してTableを作成する。

long value = 100;
long delta = 50;
StaticTableSchema<Item> schema =
StaticTableSchema.builder(Item.class)
.newItemSupplier(Item::new)
.addAttribute(String.class,
a -> a.name("ITEM_ID")
.getter(Item::getId)
.setter(Item::setId)
.tags(StaticAttributeTags.primaryPartitionKey()))
.addAttribute(Integer.class,
a -> a.name("TYPE")
.getter(Item::getType)
.setter(Item::setType)
.tags(StaticAttributeTags.primarySortKey()))
.addAttribute(Long.class,
a -> a.name("VALUE")
.getter(Item::getValue)
.setter(Item::setValue)
.tags(StaticAttributeTags.atomicCounter(value, delta)))
.build();

DynamoDbEnhancedClientからこのスキーマを利用して作成したDynamoDbTableに対してput要求/update要求を行うことで任意の値でvalueをupdateしたり、任意のdeltaでインクリメント(デクリメント)することができる。(若干無理やりではある)

任意の値でvalueを更新したい場合は、StaticTableSchemaに渡すvalueを任意の値にしてput要求すれば良い。

任意のdeltaでインクリメント(デクリメント)したい場合は、StaticTableSchemaに渡すdeltaを任意の値にしてupdate要求すれば良い。

補足

ちなみにStaticTableSchemaを利用している場合でも、@DynamoDbAtomicCounterアノテーションは付与したままで良い。

なぜなら、通常の場合@DynamoDbBeanが付与されたクラスからスキーマを生成し、DynamoDbEnhancedClientからこのスキーマを利用してDynamoDbTableを作成する。

TableSchema.fromBean(Item.class)

StaticTableSchemaを利用する場合は、Itemクラスに付与しているアノテーションは全く使われないので、共存できるということになる。

感想

@DynamoDbAtomicCounterアノテーションは利用しているサンプルが少なかったり、Googleで検索しても情報が全然出てこない。

基本すぎるからなのか、それとも誰も使おうとしていないのか分からないが、公式でも紹介しているような処理だった。

AWS SDK v2・・・まだまだ奥が深い・・・。

aws.amazon.com

Copyright (C) 2018-2022 akagoma. All Rights Reserved.