永続化エンティティ
org.springframework.data.cassandra
パッケージにある CassandraTemplate
クラス (およびそのリアクティブ版 ReactiveCassandraTemplate
) は、Spring の Cassandra サポートの中心クラスであり、データベースと対話するための豊富な機能セットを提供します。このテンプレートは、Cassandra を作成、更新、削除、クエリするための便利な操作を提供し、ドメインオブジェクトと Cassandra テーブル内の行との間のマッピングを提供します。
構成が完了すると、テンプレートインスタンスはスレッドセーフになり、複数のインスタンス間で再利用できます。 |
Cassandra の行とアプリケーションドメインクラス間のマッピングは、CassandraConverter
インターフェースの実装に委譲することによって行われます。Spring はデフォルトの実装である MappingCassandraConverter
を提供しますが、独自のカスタムコンバーターを作成することもできます。詳細については、Cassandra 変換のセクションを参照してください。
CassandraTemplate
クラスは CassandraOperations
インターフェースを実装し、そのリアクティブバリアント ReactiveCassandraTemplate
は ReactiveCassandraOperations
を実装します。[Reactive]CassandraOperations
のメソッドは、Cassandra にすでに精通している開発者にとって API が親しみやすいものとなるよう、可能な限り Cassandra で利用可能なメソッドにちなんで命名されています。
例: select
、insert
、delete
、update
などのメソッドが見つかります。設計ゴールは、ベース Cassandra ドライバーと [Reactive]CassandraOperations
の使用の間での移行をできるだけ簡単にすることでした。2 つの API の主な違いは、CassandraOperations
には CQL オブジェクトやクエリオブジェクトの代わりにドメインオブジェクトを渡すことができることです。
[Reactive]CassandraTemplate インスタンスの操作を参照するための推奨される方法は、[Reactive]CassandraOperations インターフェースを使用することです。 |
[Reactive]CassandraTemplate
で使用されるデフォルトのコンバーター実装は MappingCassandraConverter
です。MappingCassandraConverter
は追加のメタデータを使用してオブジェクトの行へのマッピングを指定できますが、フィールドとテーブル名のマッピングの規則を使用して、追加のメタデータを含まないオブジェクトを変換することもできます。これらの規則とマッピングアノテーションの使用については、「マッピング」の章で説明されています。
[Reactive]CassandraTemplate
のもう 1 つの中心的な機能は、Cassandra Java ドライバーでスローされた例外を Spring の移植可能なデータアクセス例外階層に変換することです。詳細については、例外変換に関するセクションを参照してください。
テンプレート API にはさまざまな実行モデルのフレーバーがあります。基本的な CassandraTemplate は、ブロッキング (命令型同期) 実行モデルを使用します。非同期実行および ListenableFuture インスタンスとの同期には AsyncCassandraTemplate を使用でき、リアクティブ実行には ReactiveCassandraTemplate を使用できます。 |
CassandraTemplate
のインスタンス化
CassandraTemplate
は常に Spring Bean として構成する必要がありますが、直接インスタンス化できる例を前に示しました。ただし、Spring モジュールを作成するコンテキストを想定しているため、Spring コンテナーの存在を前提としています。
Spring ApplicationContext
をロードする方法に応じて、CassandraTemplate
を取得するには 2 つの方法があります。
オートワイヤー
次の例に示すように、[Reactive]CassandraOperations
をプロジェクトにオートワイヤーできます。
命令的
リアクティブ
@Autowired
private CassandraOperations cassandraOperations;
@Autowired
private ReactiveCassandraOperations reactiveCassandraOperations;
すべての Spring オートワイヤーと同様に、これは ApplicationContext
内に型 [Reactive]CassandraOperations
の Bean が 1 つだけ存在することを前提としています。複数の [Reactive]CassandraTemplate
Bean がある場合 (同じプロジェクト内で複数のキースペースを操作する場合がこれに該当します)、@Qualifier
アノテーションを使用して、自動接続する Bean を指定できます。
命令的
リアクティブ
@Autowired
@Qualifier("keyspaceOneTemplateBeanId")
private CassandraOperations cassandraOperations;
@Autowired
@Qualifier("keyspaceOneTemplateBeanId")
private ReactiveCassandraOperations reactiveCassandraOperations;
Bean ApplicationContext
による検索
次の例に示すように、ApplicationContext
から [Reactive]CassandraTemplate
Bean を検索することもできます。
命令的
リアクティブ
CassandraOperations cassandraOperations = applicationContext.getBean("cassandraTemplate", CassandraOperations.class);
ReactiveCassandraOperations cassandraOperations = applicationContext.getBean("ReactiveCassandraOperations", ReactiveCassandraOperations.class);
行のクエリ
Query
および Criteria
クラスを使用してクエリを表現できます。これらのクラスには、lt
、lte
、is
などのネイティブ Cassandra 述語演算子名を反映するメソッド名が付いています。
Query
クラスと Criteria
クラスは流れるような API スタイルに従っているため、理解しやすいコードを使用しながら、複数のメソッド条件とクエリを簡単に チェーンで組み合わせることができます。静的インポートは、Java で Query
および Criteria
インスタンスを作成するときに読みやすさを向上させるために使用されます。
テーブル内の行のクエリ
前のセクションでは、[Reactive]CassandraTemplate
で selectOneById
メソッドを使用して単一のオブジェクトを取得する方法を説明しました。そうすると、単一のドメインオブジェクトが返されます。ドメインオブジェクトのリストとして返される行のコレクションをクエリすることもできます。名前と年齢の値がテーブルの行として格納された多数の Person
オブジェクトがあり、各個人が口座残高を持っていると仮定すると、次のコードを使用してクエリを実行できます。
[Reactive]CassandraTemplate
を使用した行のクエリ 命令的
リアクティブ
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
List<Person> result = cassandraTemplate.select(query(where("age").is(50))
.and(where("balance").gt(1000.00d)).withAllowFiltering(), Person.class);
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
Flux<Person> result = reactiveCassandraTemplate.select(query(where("age").is(50))
.and(where("balance").gt(1000.00d)).withAllowFiltering(), Person.class);
select
、selectOne
、stream
メソッドは、Query
オブジェクトをパラメーターとして受け取ります。このオブジェクトは、クエリの実行に使用される条件とオプションを定義します。この条件は、新しい Criteria
オブジェクトをインスタンス化する where
という名前の静的ファクトリメソッドを持つ Criteria
オブジェクトを使用して指定されます。クエリを読みやすくするために、org.springframework.data.cassandra.core.query.Criteria.where
および Query.query
には静的インポートを使用することをお勧めします。
このクエリは、指定された条件を満たす Person
オブジェクトのリストを返します。Criteria
クラスには、Apache Cassandra で提供される演算子に対応する次のメソッドがあります。
Criteria クラスのメソッド
CriteriaDefinition
gt(Object value)
:>
演算子を使用して基準を作成します。CriteriaDefinition
gte(Object value)
:>=
演算子を使用して基準を作成します。CriteriaDefinition
in(Object… values)
: varargs 引数にIN
演算子を使用して、基準を作成します。CriteriaDefinition
in(Collection<?> collection)
: コレクションを使用してIN
演算子を使用して、基準を作成します。CriteriaDefinition
is(Object value)
: フィールドマッチング (column = value
) を使用して基準を作成します。CriteriaDefinition
lt(Object value)
:<
演算子を使用して基準を作成します。CriteriaDefinition
lte(Object value)
:⇐
演算子を使用して基準を作成します。CriteriaDefinition
like(Object value)
:LIKE
演算子を使用して基準を作成します。CriteriaDefinition
contains(Object value)
:CONTAINS
演算子を使用して基準を作成します。CriteriaDefinition
containsKey(Object key)
:CONTAINS KEY
演算子を使用して基準を作成します。
Criteria
は、一度作成されると不変になります。
Query クラスのメソッド
Query
クラスには、クエリのオプションを提供するために使用できる追加のメソッドがいくつかあります。
Query
by(CriteriaDefinition… criteria)
:Query
オブジェクトの作成に使用されます。Query
and(CriteriaDefinition criteria)
: クエリに追加の条件を追加するために使用されます。Query
columns(Columns columns)
: クエリ結果に含める列を定義するために使用されます。Query
limit(Limit limit)
: 返される結果のサイズを指定された制限に制限するために使用されます (SELECT
制限を使用)。Query
limit(long limit)
: 返される結果のサイズを指定された制限に制限するために使用されます (SELECT
制限を使用)。Query
pageRequest(Pageable pageRequest)
:Sort
、PagingState
、fetchSize
をクエリに関連付けるために使用されます (ページングに使用されます)。Query
pagingState(ByteBuffer pagingState)
:ByteBuffer
をクエリに関連付けるために使用されます (ページングに使用されます)。Query
queryOptions(QueryOptions queryOptions)
:QueryOptions
をクエリに関連付けるために使用されます。Query
sort(Sort sort)
: 結果の並べ替え定義を提供するために使用されます。Query
withAllowFiltering()
:ALLOW FILTERING
クエリをレンダリングするために使用されます。
Query
は、一度作成されると不変になります。メソッドを呼び出すと、新しい不変 (中間) Query
オブジェクトが作成されます。
行をクエリするためのメソッド
Query
クラスには、行を返す次のメソッドがあります。
List<T>
select(Query query, Class<T> entityClass)
: テーブルから型T
のオブジェクトのリストをクエリします。T
selectOne(Query query, Class<T> entityClass)
: テーブルから型T
の単一オブジェクトをクエリします。Slice<T>
slice(Query query, Class<T> entityClass)
: テーブルから型T
のオブジェクトのSlice
をクエリすることによって、ページングを開始または継続します。Stream<T>
stream(Query query, Class<T> entityClass)
: テーブルから型T
のオブジェクトのストリームをクエリします。List<T>
select(String cql, Class<T> entityClass)
: CQL ステートメントを指定して、テーブルからT
型のオブジェクトのリストを取得するアドホッククエリ。T
selectOne(String cql, Class<T> entityClass)
: CQL ステートメントを指定して、テーブルからT
型の単一オブジェクトに対するアドホッククエリを実行します。Stream<T>
stream(String cql, Class<T> entityClass)
: CQL ステートメントを指定して、テーブルから型T
のオブジェクトのストリームに対するアドホッククエリを実行します。
クエリメソッドでは、返されるターゲット型 T
を指定する必要があります。
Fluent テンプレート API
[Reactive]CassandraOperations
インターフェースは、Apache Cassandra とのより低レベルのインタラクションに関して中心的なコンポーネントの 1 つです。幅広いメソッドを提供します。各メソッドには複数のオーバーロードがあります。そのほとんどは、API のオプション (null 可能) 部分をカバーします。
FluentCassandraOperations
とそのリアクティブバリアント ReactiveFluentCassandraOperations
は、[Reactive]CassandraOperations
の一般的なメソッドに対してより狭いインターフェースを提供し、より読みやすく流れるような API を提供します。エントリポイント (query(…)
、insert(…)
、update(…)
、delete(…)
) は、実行する操作に基づいた自然な命名スキームに従います。エントリポイントから先に進むと、API は、実際の [Reactive]CassandraOperations
を呼び出す終了メソッドに開発者を導くコンテキスト依存のメソッドのみを提供するように設計されています。次の例は、流れるような API を示しています。
命令的
リアクティブ
List<SWCharacter> all = ops.query(SWCharacter.class)
.inTable("star_wars") (1)
.all();
1 | SWCharacter が @Table でテーブル名を定義する場合、またはクラス名をテーブル名として使用しても問題ない場合は、この手順をスキップします。 |
Flux<SWCharacter> all = ops.query(SWCharacter.class)
.inTable("star_wars") (1)
.all();
1 | SWCharacter が @Table でテーブル名を定義する場合、またはクラス名をテーブル名として使用しても問題ない場合は、この手順をスキップします。 |
Cassandra のテーブルにさまざまな型のエンティティ ( SWCharacters
のテーブル内の Jedi
など) が保持されている場合、さまざまな型を使用してクエリ結果をマップできます。as(Class<?> targetType)
を使用して結果を別のターゲット型にマップできますが、query(Class<?> entityType)
は引き続きクエリとテーブル名に適用されます。次の例では、query
メソッドと as
メソッドを使用します。
命令的
リアクティブ
List<Jedi> all = ops.query(SWCharacter.class) (1)
.as(Jedi.class) (2)
.matching(query(where("jedi").is(true)))
.all();
1 | クエリフィールドは、SWCharacter 型に対してマップされます。 |
2 | 結果の行は Jedi にマップされます。 |
Flux<Jedi> all = ops.query(SWCharacter.class) (1)
.as(Jedi.class) (2)
.matching(query(where("jedi").is(true)))
.all();
1 | クエリフィールドは、SWCharacter 型に対してマップされます。 |
2 | 結果の行は Jedi にマップされます。 |
interface 型から as(Class<?>) まで指定するだけで、結果のドキュメントに射影を直接適用できます。 |
終了メソッド (first()
、one()
、all()
、stream()
) は、単一のエンティティの取得と、List
または Stream
および同様の操作としての複数のエンティティの取得の間の切り替えを処理します。
新しい流れるようなテンプレート API メソッド (つまり query(..) 、insert(..) 、update(..) 、delete(..) ) は、スレッドセーフなサポートオブジェクトを効果的に使用して CQL ステートメントを作成します。ただし、設計はさまざまな CQL ステートメントコンポーネントの final フィールドと変更に基づく構築に基づいているため、若い世代の JVM ヒープオーバーヘッドが追加されるという追加コストがかかります。多数のオブジェクトを挿入または削除する可能性がある場合 (ループ内など) には注意が必要です。 |
行の保存、更新、削除
[Reactive]CassandraTemplate
は、ドメインオブジェクトを保存、更新、削除し、それらのオブジェクトを Cassandra で管理されるテーブルにマップするための簡単なメソッドを提供します。
型マッピング
Apache Cassandra 用の Spring Data は、型のサポートを保証するために DataStax Java ドライバーの CodecRegistry
に依存しています。型が追加または変更されても、Apache Cassandra 用の Spring Data モジュールは変更を必要とせずに機能し続けます。現在の型マッピングマトリックスについては、CQL データ型 (英語) および "データマッピングと型変換" を参照してください。
行を挿入および更新するメソッド
[Reactive]CassandraTemplate
には、オブジェクトを保存および挿入するための便利な方法がいくつかあります。変換プロセスをよりきめ細かく制御するには、Spring Converter
インスタンスを MappingCassandraConverter
(たとえば、Converter<Row, Person>
) に登録できます。
挿入操作と更新操作の違いは、INSERT 操作では null 値が挿入されないことです。 |
INSERT
操作を使用する簡単な例は、POJO を保存することです。この場合、テーブル名は (完全修飾クラス名ではなく) 単純なクラス名によって決まります。オブジェクトを格納するテーブルは、マッピングメタデータを使用してオーバーライドできます。
挿入または更新する場合は、id
プロパティを設定する必要があります。Apache Cassandra には ID を生成する手段がありません。
次の例では、保存操作を使用してその内容を取得します。
[Reactive]CassandraTemplate
を使用したオブジェクトの挿入と取得 命令的
リアクティブ
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
Person bob = new Person("Bob", 33);
cassandraTemplate.insert(bob);
Person queriedBob = cassandraTemplate.selectOneById(query(where("age").is(33)), Person.class);
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
Person bob = new Person("Bob", 33);
cassandraTemplate.insert(bob);
Mono<Person> queriedBob = reactiveCassandraTemplate.selectOneById(query(where("age").is(33)), Person.class);
次の操作を使用して挿入および保存できます。
void
insert(Object objectToSave)
: Apache Cassandra テーブルにオブジェクトを挿入します。WriteResult
insert(Object objectToSave, InsertOptions options)
: Apache Cassandra テーブルにオブジェクトを挿入し、InsertOptions
を適用します。
次の更新操作を使用できます。
void
update(Object objectToSave)
: Apache Cassandra テーブル内のオブジェクトを更新します。WriteResult
update(Object objectToSave, UpdateOptions options)
: Apache Cassandra テーブル内のオブジェクトを更新し、UpdateOptions
を適用します。
次の例に示すように、昔ながらの方法を使用して独自の CQL ステートメントを作成することもできます。
命令的
リアクティブ
String cql = "INSERT INTO person (age, name) VALUES (39, 'Bob')";
cassandraTemplate().getCqlOperations().execute(cql);
String cql = "INSERT INTO person (age, name) VALUES (39, 'Bob')";
Mono<Boolean> applied = reactiveCassandraTemplate.getReactiveCqlOperations().execute(cql);
InsertOptions
および UpdateOptions
を使用する場合は、TTL、整合性レベル、軽量トランザクションなどの追加オプションを構成することもできます。
行はどのテーブルに挿入されますか ?
テーブルの操作に使用されるテーブル名は 2 つの方法で管理できます。デフォルトのテーブル名は、小文字で始まるように変更された単純なクラス名です。com.example.Person
クラスのインスタンスは person
テーブルに格納されます。2 番目の方法は、@Table
アノテーションでテーブル名を指定することです。
バッチ内の個々のオブジェクトの挿入、更新、削除
Cassandra プロトコルは、バッチを使用した 1 回の操作で行のコレクションを挿入することをサポートしています。
[Reactive]CassandraTemplate
インターフェースの次のメソッドは、この機能をサポートしています。
batchOps
: 新しい[Reactive]CassandraBatchOperations
を作成してバッチに追加します。
[Reactive]CassandraBatchOperations
insert
: 単一のオブジェクト、配列 (var-args)、またはオブジェクトのIterable
を挿入して受け取ります。update
: 単一のオブジェクト、配列 (var-args)、またはオブジェクトのIterable
を更新対象として受け取ります。delete
: 単一のオブジェクト、配列 (var-args)、または削除するオブジェクトのIterable
を受け取ります。withTimestamp
: TTL をバッチに適用します。execute
: バッチを実行します。
テーブル内の行を更新する
更新の場合、複数の行を更新することを選択できます。
次の例は、+
割り当てを使用して残高に 1 回限りの $50.00 ボーナスを追加することによって、単一のアカウントオブジェクトを更新することを示しています。
[Reactive]CasandraTemplate
を使用した行の更新 命令的
リアクティブ
import static org.springframework.data.cassandra.core.query.Criteria.where;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
…
boolean applied = cassandraTemplate.update(Query.query(where("id").is("foo")),
Update.create().increment("balance", 50.00), Account.class);
import static org.springframework.data.cassandra.core.query.Criteria.where;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
…
Mono<Boolean> wasApplied = reactiveCassandraTemplate.update(Query.query(where("id").is("foo")),
Update.create().increment("balance", 50.00), Account.class);
前述の Query
に加えて、Update
オブジェクトを使用して更新定義を提供します。Update
クラスには、Apache Cassandra で使用可能な更新割り当てに一致するメソッドがあります。
ほとんどのメソッドは Update
オブジェクトを返し、コードスタイルの目的で流れるような API を提供します。
行の更新を実行するメソッド
update メソッドは次のように行を更新できます。
boolean
update(Query query, Update update, Class<?> entityClass)
: Apache Cassandra テーブル内のオブジェクトの選択を更新します。
Update クラスのメソッド
Update
クラスは、そのメソッドが連鎖することを目的としているため、少しの「構文糖」を加えて使用できます。また、静的メソッド public static Update update(String key, Object value)
と静的インポートを使用して、新しい Update
インスタンスの作成を開始することもできます。
Update
クラスには次のメソッドがあります。
AddToBuilder
addTo(String columnName)
AddToBuilder
エントリポイント:prepend(Object value)
の更新:+
更新割り当てを使用して、コレクション値を既存のコレクションの先頭に追加します。prependAll(Object… values)
の更新:+
更新割り当てを使用して、すべてのコレクション値を既存のコレクションの先頭に追加します。append(Object value)
の更新:+
更新割り当てを使用して、コレクション値を既存のコレクションに追加します。append(Object… values)
の更新:+
更新割り当てを使用して、すべてのコレクション値を既存のコレクションに追加します。entry(Object key, Object value)
の更新:+
更新割り当てを使用してマップエントリを追加します。addAll(Map<? extends Object, ? extends Object> map)
の更新:+
更新割り当てを使用して、すべてのマップエントリをマップに追加します。
Update
remove(String columnName, Object value)
:-
更新割り当てを使用して、コレクションから値を削除します。Update
clear(String columnName)
: コレクションをクリアします。Update
increment(String columnName, Number delta)
:+
更新割り当てを使用して更新します。Update
decrement(String columnName, Number delta)
:-
更新割り当てを使用して更新します。Update
set(String columnName, Object value)
:=
更新割り当てを使用して更新します。SetBuilder
セット(String columnName)
SetBuilder
エントリポイント:atIndex(int index).to(Object value)
の更新:=
更新割り当てを使用して、指定されたインデックスのコレクションを値に設定します。atKey(String object).to(Object value)
の更新: 指定されたキーのマップエントリを=
更新割り当ての値に設定します。
次のリストは、いくつかの更新例を示しています。
// UPDATE … SET key = 'Spring Data';
Update.update("key", "Spring Data")
// UPDATE … SET key[5] = 'Spring Data';
Update.empty().set("key").atIndex(5).to("Spring Data");
// UPDATE … SET key = key + ['Spring', 'DATA'];
Update.empty().addTo("key").appendAll("Spring", "Data");
Update
は一度作成されると不変であることに注意してください。メソッドを呼び出すと、新しい不変 (中間) Update
オブジェクトが作成されます。
行を削除する方法
次のオーバーロードされたメソッドを使用して、データベースからオブジェクトを削除できます。
boolean
delete(Query query, Class<?> entityClass)
:Query
で選択したオブジェクトを削除します。T
delete(T entity)
: 指定されたオブジェクトを削除します。T
delete(T entity, QueryOptions queryOptions)
:QueryOptions
を適用して指定されたオブジェクトを削除します。boolean
deleteById(Object id, Class<?> entityClass)
: 指定された ID を使用してオブジェクトを削除します。
楽観的ロック
@Version
アノテーションは、Cassandra のコンテキストにおける JPA と同様の構文を提供し、バージョンが一致する行にのみ更新が適用されるようにします。オプティミスティックロックは、Cassandra の軽量トランザクションを利用して、条件付きで行を挿入、更新、削除します。INSERT
ステートメントは IF NOT EXISTS
条件で実行されます。更新と削除の場合、バージョンプロパティの実際の値が UPDATE
条件に追加され、その間に別の操作で行が変更された場合でも変更は影響しません。その場合、OptimisticLockingFailureException
がスローされます。次の例は、これらの機能を示しています。
@Table
class Person {
@Id String id;
String firstname;
String lastname;
@Version Long version;
}
Person daenerys = template.insert(new Person("Daenerys")); (1)
Person tmp = template.findOne(query(where("id").is(daenerys.getId())), Person.class); (2)
daenerys.setLastname("Targaryen");
template.save(daenerys); (3)
template.save(tmp); // throws OptimisticLockingFailureException (4)
1 | 最初にドキュメントを挿入します。version は 0 に設定されます。 |
2 | 挿入したばかりのドキュメントを読み込みます。version は 0 のままです。 |
3 | version = 0 でドキュメントを更新します。lastname を設定し、version を 1 にバンプします。 |
4 | version = 0 がまだある、以前にロードされたドキュメントを更新してみてください。現在の version は 1 であるため、操作は OptimisticLockingFailureException で失敗します。 |
オプティミスティックロックは単一エンティティ操作でのみサポートされ、バッチ操作ではサポートされません。 |