ドキュメントの保存、更新、削除
MongoTemplate
/ ReactiveMongoTemplatge
を使用すると、ドメインオブジェクトを保存、更新、削除し、それらのオブジェクトを MongoDB に保存されているドキュメントにマップできます。命令型 API とリアクティブ API の API シグネチャーは、戻り値の型が異なるだけで、ほとんど同じです。同期 API は void
、単一 Object
および List
を使用しますが、リアクティブ API は Mono<Void>
、Mono<Object>
、Flux
で構成されます。
次のクラスを検討してください。
public class Person {
private String id;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
前の例の Person
クラスを考慮すると、次の例に示すように、オブジェクトを保存、更新、削除できます。
命令的
リアクティブ
public class MongoApplication {
private static final Log log = LogFactory.getLog(MongoApplication.class);
public static void main(String[] args) {
MongoOperations template = new MongoTemplate(new SimpleMongoClientDbFactory(MongoClients.create(), "database"));
Person p = new Person("Joe", 34);
// Insert is used to initially store the object into the database.
template.insert(p);
log.info("Insert: " + p);
// Find
p = template.findById(p.getId(), Person.class);
log.info("Found: " + p);
// Update
template.updateFirst(query(where("name").is("Joe")), update("age", 35), Person.class);
p = template.findOne(query(where("name").is("Joe")), Person.class);
log.info("Updated: " + p);
// Delete
template.remove(p);
// Check that deletion worked
List<Person> people = template.findAll(Person.class);
log.info("Number of people = : " + people.size());
template.dropCollection(Person.class);
}
}
前述の例では、次のログ出力が生成されます ( MongoTemplate
からのデバッグメッセージを含む)。
DEBUG apping.MongoPersistentEntityIndexCreator: 80 - Analyzing class class org.spring.example.Person for index information.
DEBUG work.data.mongodb.core.MongoTemplate: 632 - insert Document containing fields: [_class, age, name] in collection: person
INFO org.spring.example.MongoApp: 30 - Insert: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34]
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "_id" : { "$oid" : "4ddc6e784ce5b1eba3ceaf5c"}} in db.collection: database.person
INFO org.spring.example.MongoApp: 34 - Found: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34]
DEBUG work.data.mongodb.core.MongoTemplate: 778 - calling update using query: { "name" : "Joe"} and update: { "$set" : { "age" : 35}} in collection: person
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "name" : "Joe"} in db.collection: database.person
INFO org.spring.example.MongoApp: 39 - Updated: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=35]
DEBUG work.data.mongodb.core.MongoTemplate: 823 - remove using query: { "id" : "4ddc6e784ce5b1eba3ceaf5c"} in collection: person
INFO org.spring.example.MongoApp: 46 - Number of people = : 0
DEBUG work.data.mongodb.core.MongoTemplate: 376 - Dropped collection [database.person]
public class ReactiveMongoApplication {
private static final Logger log = LoggerFactory.getLogger(ReactiveMongoApplication.class);
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
ReactiveMongoTemplate template = new ReactiveMongoTemplate(MongoClients.create(), "database");
template.insert(new Person("Joe", 34)).doOnNext(person -> log.info("Insert: " + person))
.flatMap(person -> template.findById(person.getId(), Person.class))
.doOnNext(person -> log.info("Found: " + person))
.zipWith(person -> template.updateFirst(query(where("name").is("Joe")), update("age", 35), Person.class))
.flatMap(tuple -> template.remove(tuple.getT1())).flatMap(deleteResult -> template.findAll(Person.class))
.count().doOnSuccess(count -> {
log.info("Number of people: " + count);
latch.countDown();
})
.subscribe();
latch.await();
}
}
MongoConverter
は、Id
プロパティ名を (規則に従って) 認識することにより、データベースに格納されている String
と ObjectId
の間で暗黙的な変換を引き起こしました。
前述の例は、MongoTemplate
/ ReactiveMongoTemplate
での保存、更新、削除操作の使用方法を示すことを目的としており、複雑なマッピング機能を示すことを目的としたものではありません。前の例で使用されているクエリ構文については、"ドキュメントのクエリ" セクションで詳しく説明します。
MongoDB では、すべてのドキュメントに _id フィールドが必要です。このフィールドの特殊な処理の詳細については、ID の取り扱いセクションを参照してください。 |
MongoDB コレクションには、さまざまな型のインスタンスを表すドキュメントを含めることができます。詳細については、型 マッピングを参照してください。 |
挿入 / 保存
MongoTemplate
には、オブジェクトを保存および挿入するための便利なメソッドがいくつかあります。変換プロセスをよりきめ細かく制御するには、Spring コンバーターを MappingMongoConverter
に登録できます (例: Converter<Person, Document>
および Converter<Document, Person>
)。
挿入操作と保存操作の違いは、オブジェクトがまだ存在しない場合、保存操作では挿入が実行されることです。 |
保存操作を使用する簡単な例は、POJO を保存することです。この場合、コレクション名はクラスの名前 (完全修飾名ではありません) によって決まります。特定のコレクション名を使用して保存操作を呼び出すこともできます。マッピングメタデータを使用して、オブジェクトを保存するコレクションをオーバーライドできます。
挿入または保存するときに、Id
プロパティが設定されていない場合、その値はデータベースによって自動生成されると想定されます。ObjectId
の自動生成が成功するには、クラス内の Id
プロパティまたはフィールドの型が String
、ObjectId
、または BigInteger
である必要があります。
次の例は、ドキュメントを保存し、その内容を取得する方法を示しています。
命令的
リアクティブ
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Criteria.query;
//...
template.insert(new Person("Bob", 33));
Person person = template.query(Person.class)
.matching(query(where("age").is(33)))
.oneValue();
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Criteria.query;
//...
Mono<Person> person = mongoTemplate.insert(new Person("Bob", 33))
.then(mongoTemplate.query(Person.class)
.matching(query(where("age").is(33)))
.one());
次の挿入および保存操作が利用可能です。
void
save(Object objectToSave)
: オブジェクトをデフォルトのコレクションに保存します。void
save(Object objectToSave, String collectionName)
: オブジェクトを指定されたコレクションに保存します。
同様の挿入操作のセットも利用できます。
void
insert(Object objectToSave)
: オブジェクトをデフォルトのコレクションに挿入します。void
insert(Object objectToSave, String collectionName)
: 指定されたコレクションにオブジェクトを挿入します。
_id
フィールドがマッピング層でどのように処理されるか
MongoDB では、すべてのドキュメントに _id
フィールドが必要です。これを指定しない場合、サーバーは識別子の種類を認識しないため、ドライバーはドメインモデルを考慮せずに生成された値を ObjectId
に割り当てます。MappingMongoConverter
を使用する場合、Java クラスのプロパティがこの _id
フィールドにマップされる方法は、特定のルールによって決まります。
@Id
(org.springframework.data.annotation.Id
) のアノテーションが付けられたプロパティまたはフィールドは、_id
フィールドにマップされます。アノテーションのない
id
という名前のプロパティまたはフィールドは、_id
フィールドにマップされます。
以下に、MappingMongoConverter
( MongoTemplate
のデフォルト) を使用する場合に、_id
ドキュメントフィールドにマップされたプロパティでどのような型変換が行われるか (存在する場合) を概説します。
可能であれば、Java クラスで
String
として宣言されたid
プロパティまたはフィールドは、SpringConverter<String, ObjectId>
を使用してObjectId
に変換され、格納されます。有効な変換ルールは MongoDB Java ドライバーに委譲されます。ObjectId
に変換できない場合、値は文字列としてデータベースに保存されます。Java クラスで
BigInteger
として宣言されたid
プロパティまたはフィールドは、SpringConverter<BigInteger, ObjectId>
を使用してObjectId
に変換され、格納されます。
前述のルールセットで指定されたフィールドまたはプロパティが Java クラスに存在しない場合、暗黙的な _id
ファイルがドライバーによって生成されますが、Java クラスのプロパティまたはフィールドにはマップされません。
クエリと更新の際、MongoTemplate
はドキュメントを保存するための前述のルールに対応するコンバーターを使用して、クエリで使用されるフィールド名と型がドメインクラスの内容と一致できるようにします。
一部の環境では、Spring Data マッピングレイヤーを介して実行されなかった MongoDB に格納されたデータなど、Id
値をマッピングするためのカスタマイズされたアプローチが必要です。ドキュメントには、ObjectId
または String
として表現できる _id
値を含めることができます。ストアからドメイン型にドキュメントを読み戻すことは問題なく機能します。id
を介したドキュメントのクエリは、暗黙的な ObjectId
変換により面倒になる場合があります。このメソッドではドキュメントを取得できません。このような場合、@MongoId
は実際の ID マッピングの試行をより詳細に制御できます。
@MongoId
マッピング public class PlainStringId {
@MongoId String id; (1)
}
public class PlainObjectId {
@MongoId ObjectId id; (2)
}
public class StringToObjectId {
@MongoId(FieldType.OBJECT_ID) String id; (3)
}
1 | ID はそれ以上変換されずに String として扱われます。 |
2 | ID は ObjectId として扱われます。 |
3 | 指定された String が有効な ObjectId 16 進数である場合、ID は ObjectId として扱われ、それ以外の場合は String として扱われます。@Id 用途に対応します。 |
ドキュメントはどのコレクションに保存されますか ?
ドキュメントに使用されるコレクション名を管理するには 2 つの方法があります。使用されるデフォルトのコレクション名は、小文字で始まるように変更されたクラス名です。com.test.Person
クラスは person
コレクションに格納されます。@Document
アノテーションを使用して別のコレクション名を指定することで、これをカスタマイズできます。選択した MongoTemplate
メソッド呼び出しの最後のパラメーターとして独自のコレクション名を指定することで、コレクション名をオーバーライドすることもできます。
個々のオブジェクトの挿入または保存
MongoDB ドライバーは、1 回の操作でドキュメントのコレクションを挿入することをサポートしています。MongoOperations
インターフェースの次のメソッドは、この機能をサポートしています。
入れる : オブジェクトを挿入します。同じ
id
を持つ既存のドキュメントが存在する場合、エラーが生成されます。insertAll: 最初のパラメーターとしてオブジェクトの
Collection
を取ります。このメソッドは各オブジェクトをインスペクションし、前に指定したルールに基づいて適切なコレクションに挿入します。保存 : オブジェクトを保存し、同じ
id
を持つ可能性のあるオブジェクトを上書きします。
複数のオブジェクトをバッチに挿入する
MongoDB ドライバーは、1 回の操作でドキュメントのコレクションを挿入することをサポートしています。MongoOperations
インターフェースの次のメソッドは、insert
または専用の BulkOperations
インターフェースを介してこの機能をサポートします。
命令的
リアクティブ
Collection<Person> inserted = template.insert(List.of(...), Person.class);
Flux<Person> inserted = template.insert(List.of(...), Person.class);
命令的
リアクティブ
BulkWriteResult result = template.bulkOps(BulkMode.ORDERED, Person.class)
.insert(List.of(...))
.execute();
Mono<BulkWriteResult> result = template.bulkOps(BulkMode.ORDERED, Person.class)
.insert(List.of(...))
.execute();
バッチとバルクのサーバーパフォーマンスは同じです。ただし、一括操作ではライフサイクルイベントが公開されません。 |
insert を呼び出す前に設定されていない |
更新
更新の場合は、MongoOperation.updateFirst
を使用して最初に見つかったドキュメントを更新することも、MongoOperation.updateMulti
メソッドまたは Fluent API の all
を使用して、クエリに一致することが見つかったすべてのドキュメントを更新することもできます。次の例は、$inc
演算子を使用して残高に 1 回限りの $50.00 ボーナスを追加するすべての SAVINGS
アカウントの更新を示しています。
MongoTemplate
/ ReactiveMongoTemplate
を使用したドキュメントの更新 命令的
リアクティブ
import static org.springframework.data.mongodb.core.query.Criteria.where;
import org.springframework.data.mongodb.core.query.Update;
// ...
UpdateResult result = template.update(Account.class)
.matching(where("accounts.accountType").is(Type.SAVINGS))
.apply(new Update().inc("accounts.$.balance", 50.00))
.all();
import static org.springframework.data.mongodb.core.query.Criteria.where;
import org.springframework.data.mongodb.core.query.Update;
// ...
Mono<UpdateResult> result = template.update(Account.class)
.matching(where("accounts.accountType").is(Type.SAVINGS))
.apply(new Update().inc("accounts.$.balance", 50.00))
.all();
前に説明した Query
に加えて、Update
オブジェクトを使用して更新定義を提供します。Update
クラスには、MongoDB で使用できる更新修飾子に一致するメソッドがあります。ほとんどのメソッドは Update
オブジェクトを返し、API に流れるようなスタイルを提供します。
|
ドキュメントの更新を実行するためのメソッド
updateFirst: クエリドキュメント条件に一致する最初のドキュメントを更新されたドキュメントで更新します。
updateMulti: クエリドキュメント条件に一致するすべてのオブジェクトを更新されたドキュメントで更新します。
updateFirst は順序付けをサポートしていません。Sort を適用するには findAndModify を使用してください。 |
更新操作のインデックスヒントは、Query.withHint(…) 経由で提供できます。 |
Update
クラスのメソッド
Update
クラスのメソッドは連鎖することを目的としているため、Update
クラスで少しの「構文糖」を使用できます。また、public static Update update(String key, Object value)
と静的インポートを使用して、新しい Update
インスタンスの作成を開始することもできます。
Update
クラスには次のメソッドが含まれています。
Update
addToSet(String key, Object value)
$addToSet
更新修飾子を使用した更新Update
currentDate(String key)
$currentDate
更新修飾子を使用した更新Update
currentTimestamp(String key)
$type
timestamp
を使用した$currentDate
更新修飾子を使用した更新Update
inc(String key, Number inc)
$inc
更新修飾子を使用した更新Update
最大(String key, Object max)
$max
更新修飾子を使用した更新Update
最小$min
更新修飾子を使用した(String key, Object min)
更新Update
$mul
更新修飾子を使用した(String key, Number multiplier)
更新の乗算Update
ポップ(String key, Update.Position pos)
$pop
更新修飾子を使用した更新Update
$pull
更新修飾子を使用した(String key, Object value)
更新のプルUpdate
pullAll(String key, Object[] values)
$pullAll
更新修飾子を使用した更新Update
プッシュ$push
更新修飾子を使用した(String key, Object value)
更新Update
pushAll(String key, Object[] values)
$pushAll
更新修飾子を使用した更新Update
$rename
更新修飾子を使用した(String oldName, String newName)
Update の名前変更Update
セット$set
更新修飾子を使用した(String key, Object value)
更新Update
setOnInsert(String key, Object value)
$setOnInsert
更新修飾子を使用した更新Update
設定解除(String key)
$unset
更新修飾子を使用した更新
$push
や $addToSet
などの一部の更新修飾子では、追加の演算子のネストが可能です。
// { $push : { "category" : { "$each" : [ "spring" , "data" ] } } }
new Update().push("category").each("spring", "data")
// { $push : { "key" : { "$position" : 0 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
new Update().push("key").atPosition(Position.FIRST).each(Arrays.asList("Arya", "Arry", "Weasel"));
// { $push : { "key" : { "$slice" : 5 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
new Update().push("key").slice(5).each(Arrays.asList("Arya", "Arry", "Weasel"));
// { $addToSet : { "values" : { "$each" : [ "spring" , "data" , "mongodb" ] } } }
new Update().addToSet("values").each("spring", "data", "mongodb");
集約パイプラインの更新
MongoOperations
および ReactiveMongoOperations
によって公開される更新メソッドは、AggregationUpdate
経由で集約パイプラインも受け入れます。AggregationUpdate
を使用すると、更新操作で MongoDB 4.2 アグリゲーション (英語) を利用できるようになります。更新で集計を使用すると、1 回の操作で複数のステージと複数の条件を表現することにより、1 つ以上のフィールドを更新できます。
更新は次の段階で構成されます。
AggregationUpdate.set(…).toValue(…)
→$set : { … }
AggregationUpdate.unset(…)
→$unset : [ … ]
AggregationUpdate.replaceWith(…)
→$replaceWith : { … }
AggregationUpdate update = Aggregation.newUpdate()
.set("average").toValue(ArithmeticOperators.valueOf("tests").avg()) (1)
.set("grade").toValue(ConditionalOperators.switchCases( (2)
when(valueOf("average").greaterThanEqualToValue(90)).then("A"),
when(valueOf("average").greaterThanEqualToValue(80)).then("B"),
when(valueOf("average").greaterThanEqualToValue(70)).then("C"),
when(valueOf("average").greaterThanEqualToValue(60)).then("D"))
.defaultTo("F")
);
template.update(Student.class) (3)
.apply(update)
.all(); (4)
db.students.update( (3)
{ },
[
{ $set: { average : { $avg: "$tests" } } }, (1)
{ $set: { grade: { $switch: { (2)
branches: [
{ case: { $gte: [ "$average", 90 ] }, then: "A" },
{ case: { $gte: [ "$average", 80 ] }, then: "B" },
{ case: { $gte: [ "$average", 70 ] }, then: "C" },
{ case: { $gte: [ "$average", 60 ] }, then: "D" }
],
default: "F"
} } } }
],
{ multi: true } (4)
)
1 | 最初の $set ステージでは、テストフィールドの平均に基づいて新しいフィールドの平均を計算します。 |
2 | 2 番目の $set ステージは、最初の集計ステージで計算された平均フィールドに基づいて、新しいフィールドグレードを計算します。 |
3 | パイプラインは学生コレクション上で実行され、集計フィールドマッピングに Student を使用します。 |
4 | コレクション内の一致するすべてのドキュメントに更新を適用します。 |
アップサート
updateFirst
操作の実行に関連して、クエリに一致するドキュメントが見つからない場合に挿入を実行する upsert
操作を実行することもできます。挿入されるドキュメントは、クエリドキュメントと更新ドキュメントの組み合わせです。次の例は、upsert
メソッドの使用方法を示しています。
命令的
リアクティブ
UpdateResult result = template.update(Person.class)
.matching(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update"))
.apply(update("address", addr))
.upsert();
Mono<UpdateResult> result = template.update(Person.class)
.matching(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update"))
.apply(update("address", addr))
.upsert();
upsert は順序付けをサポートしていません。Sort を適用するには findAndModify を使用してください。 |
|
コレクション内のドキュメントの置換
MongoTemplate
経由で利用できるさまざまな replace
メソッドを使用すると、最初に一致したドキュメントをオーバーライドできます。一致するものが見つからない場合は、ReplaceOptions
に適切な構成を提供することで、(前のセクションで概説したように) 新しいものを更新 / 挿入できます。
Person tom = template.insert(new Person("Motte", 21)); (1)
Query query = Query.query(Criteria.where("firstName").is(tom.getFirstName())); (2)
tom.setFirstname("Tom"); (3)
template.replace(query, tom, ReplaceOptions.none()); (4)
1 | 新しいドキュメントを挿入します。 |
2 | 置換する単一のドキュメントを識別するために使用されるクエリ。 |
3 | 既存と同じ _id を保持するか、_id をまったく保持しない置換ドキュメントを設定します。 |
4 | 置換操作を実行します。1 つを Upsert に置き換えます |
Person tom = new Person("id-123", "Tom", 21) (1) Query query = Query.query(Criteria.where("firstName").is(tom.getFirstName())); template.replace(query, tom, ReplaceOptions.replaceOptions().upsert()); (2)
1 | 更新 / 挿入には _id 値が存在する必要があります。そうでない場合、MongoDB は、ドメイン型に互換性のない ObjectId を使用して新しい値を作成する可能性があります。MongoDB はドメイン型を認識しないため、@Field(targetType) ヒントは考慮されず、結果の ObjectId はドメインモデルと互換性がない可能性があります。 |
2 | 一致するものが見つからない場合は、upsert を使用して新しいドキュメントを挿入します |
置換操作で既存のドキュメントの |
検索と変更
MongoCollection
の findAndModify(…)
メソッドは、ドキュメントを更新し、1 回の操作で古いドキュメントまたは新しく更新されたドキュメントを返すことができます。MongoTemplate
は、Query
および Update
クラスを受け取り、Document
から POJO に変換する 4 つの findAndModify
オーバーロードメソッドを提供します。
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass, String collectionName);
次の例では、いくつかの Person
オブジェクトをコンテナーに挿入し、findAndUpdate
操作を実行します。
template.insert(new Person("Tom", 21));
template.insert(new Person("Dick", 22));
template.insert(new Person("Harry", 23));
Query query = new Query(Criteria.where("firstName").is("Harry"));
Update update = new Update().inc("age", 1);
Person oldValue = template.update(Person.class)
.matching(query)
.apply(update)
.findAndModifyValue(); // oldValue.age == 23
Person newValue = template.query(Person.class)
.matching(query)
.findOneValue(); // newValye.age == 24
Person newestValue = template.update(Person.class)
.matching(query)
.apply(update)
.withOptions(FindAndModifyOptions.options().returnNew(true)) // Now return the newly updated document when updating
.findAndModifyValue(); // newestValue.age == 25
FindAndModifyOptions
メソッドを使用すると、returnNew
、upsert
、remove
のオプションを設定できます。前のコードスニペットを継承した例は次のとおりです。
Person upserted = template.update(Person.class)
.matching(new Query(Criteria.where("firstName").is("Mary")))
.apply(update)
.withOptions(FindAndModifyOptions.options().upsert(true).returnNew(true))
.findAndModifyValue()
|
検索と置換
Document
全体を置き換える最も簡単な方法は、save
メソッドを使用して id
を使用することです。ただし、これは常に実現可能であるとは限りません。findAndReplace
は、単純なクエリを通じて置換するドキュメントを特定できる代替手段を提供します。
Optional<User> result = template.update(Person.class) (1)
.matching(query(where("firstame").is("Tom"))) (2)
.replaceWith(new Person("Dick"))
.withOptions(FindAndReplaceOptions.options().upsert()) (3)
.as(User.class) (4)
.findAndReplace(); (5)
1 | クエリのマッピングとコレクション名の導出に指定されたドメイン型で Fluent Update API を使用するか、単に MongoOperations#findAndReplace を使用します。 |
2 | 指定されたドメイン型に対してマッピングされた実際の一致クエリ。クエリを介して sort 、fields 、collation 設定を提供します。 |
3 | upsert など、デフォルト以外のオプションを提供する追加のオプションのフック。 |
4 | 操作結果のマッピングに使用されるオプションの射影型。何も指定されていない場合は、初期ドメイン型が使用されます。 |
5 | 実際の処理をトリガーします。null 許容の結果を取得するには、Optional の代わりに findAndReplaceValue を使用します。 |
既存の Document の id はストア自体によって交換品に引き継がれるため、交換品は id 自体を保持してはいけないことに注意してください。また、findAndReplace は、指定された並べ替え順序に応じて、クエリ条件に一致する最初のドキュメントのみを置き換えることにも注意してください。 |
削除
5 つのオーバーロードされたメソッドのいずれかを使用して、データベースからオブジェクトを削除できます。
template.remove(tywin, "GOT"); (1)
template.remove(query(where("lastname").is("lannister")), "GOT"); (2)
template.remove(new Query().limit(3), "GOT"); (3)
template.findAllAndRemove(query(where("lastname").is("lannister"), "GOT"); (4)
template.findAllAndRemove(new Query().limit(3), "GOT"); (5)
1 | _id で指定された単一のエンティティを、関連付けられたコレクションから削除します。 |
2 | クエリの条件に一致するすべてのドキュメントを GOT コレクションから削除します。 |
3 | GOT コレクションの最初の 3 つのドキュメントを削除します。<2> とは異なり、削除するドキュメントは _id によって識別され、指定されたクエリを実行し、最初に sort 、limit 、skip オプションを適用してから、別の手順ですべてを一度に削除します。 |
4 | クエリの条件に一致するすべてのドキュメントを GOT コレクションから削除します。<3> とは異なり、ドキュメントは一括で削除されず、1 つずつ削除されます。 |
5 | GOT コレクションの最初の 3 つのドキュメントを削除します。<3> とは異なり、ドキュメントは一括で削除されず、1 つずつ削除されます。 |
楽観的ロック
@Version
アノテーションは、MongoDB のコンテキストで JPA と同様の構文を提供し、バージョンが一致するドキュメントにのみ更新が適用されるようにします。バージョンプロパティの実際の値は、その間に別の操作でドキュメントが変更された場合でも更新が影響を及ぼさないように更新クエリに追加されます。その場合、OptimisticLockingFailureException
がスローされます。次の例は、これらの機能を示しています。
@Document
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 で失敗します。 |
MongoTemplate
上の特定の CRUD 操作のみがバージョンプロパティを考慮して変更します。詳細については、MongoOperations
java のドキュメントを参照してください。
オプティミスティックロックでは、WriteConcern を ACKNOWLEDGED に設定する必要があります。そうしないと、OptimisticLockingFailureException が静かに飲み込まれる可能性があります。 |
バージョン 2.2 以降、データベースからエンティティを削除する場合、MongoOperations には @Version プロパティも含まれます。バージョンチェックを行わずに Document を削除するには、MongoOperations#remove(Object) の代わりに MongoOperations#remove(Query,…) を使用します。 |
バージョン 2.2 以降、リポジトリは、バージョン管理されたエンティティを削除するときに、承認された削除の結果をチェックします。OptimisticLockingFailureException は、バージョン管理されたエンティティを CrudRepository.delete(Object) を通じて削除できない場合に発生します。このような場合は、その間にバージョンが変更されたか、オブジェクトが削除されました。CrudRepository.deleteById(ID) を使用すると、オプティミスティックロック機能をバイパスし、バージョンに関係なくオブジェクトを削除できます。 |