Menu Docs
Página inicial do Docs
/ / /
Java síncrono
/ /

Operações compostas

Nesta página

  • Visão geral
  • Como usar operações compostas
  • Encontrar e atualizar
  • Encontrar e substituir
  • Localize e exclua
  • Evitando uma condição de corrida
  • Exemplo com condição de corrida
  • Exemplo sem condição de corrida

Neste guia, você verá como realizar operações compostas com o driver Java do MongoDB.

As operações compostas consistem em uma operação de leitura e gravação executada como uma operação atômica. Uma operação atômica é uma operação que é concluída totalmente ou não é concluída. As operações atômicas não podem ser concluídas parcialmente.

As operações atômicas podem ajudar a evitar condições de corrida em seu código. Uma condição de corrida ocorre quando o comportamento do seu código depende da ordem de eventos incontroláveis.

O MongoDB é compatível com as seguintes operações compostas:

  • Localizar e atualizar um documento

  • Localizar e substituir um documento

  • Encontrar e excluir um documento

Se você precisar executar tarefas mais complexas atomicamente, como ler e gravar em mais de um documento, use transações. As transação são uma funcionalidade do MongoDB e de outros reconhecimento de data center que permitem definir uma sequência arbitrária de reconhecimento de data center como uma operação atômica.

Para obter mais informações sobre operações atômicas e atomicidade, consulte a entrada manual do MongoDB para atomicidade e transações.

Para obter mais informações sobre transações, consulte a entrada manual do MongoDB para transações.

Esta seção mostra como usar cada operação composta com o MongoDB Java Driver.

Os exemplos a seguir usam uma collection que contém esses dois documentos de amostra.

{"_id": 1, "food": "donut", "color": "green"}
{"_id": 2, "food": "pear", "color": "yellow"}

O código completo para os exemplos a seguir está disponível no Github aqui.

Observação

Antes ou depois da gravação?

Por padrão, cada operação composta retorna o documento encontrado no estado anterior à operação de gravação. Você pode recuperar o documento encontrado no estado após a operação de gravação usando a classe de opções correspondente à operação composta. Você pode ver um exemplo dessa configuração no exemplo Localizar e Substituir abaixo.

Para localizar e atualizar um documento, utilize o método findOneAndUpdate() da classe MongoCollection . O método findOneAndUpdate() retorna o documento encontrado ou null se nenhum documento corresponder à sua query.

O exemplo a seguir usa o método findOneAndUpdate() para localizar um documento com o campo color definido como "green" e atualizar o campo food nesse documento para "pizza".

O exemplo também utiliza uma instância do FindOneAndUpdateOptions para especificar as seguintes opções:

  • Exclua o campo _id do documento encontrado com uma projeção.

  • Especifique um upsert, que insere o documento especificado pelo filtro de query se nenhum documento corresponder à query.

  • Defina um tempo máximo de execução de cinco segundos para esta operação na instância do MongoDB. Se a operação demorar mais, o método findOneAndUpdate() lançará um MongoExecutionTimeoutException.

// <MongoCollection set up code here>
// Creates a projection to exclude the "_id" field from the retrieved documents
Bson projection = Projections.excludeId();
// Creates a filter to match documents with a "color" value of "green"
Bson filter = Filters.eq("color", "green");
// Creates an update document to set the value of "food" to "pizza"
Bson update = Updates.set("food", "pizza");
// Defines options that specify projected fields, permit an upsert and limit execution time
FindOneAndUpdateOptions options = new FindOneAndUpdateOptions().
projection(projection).
upsert(true).
maxTime(5, TimeUnit.SECONDS);
// Updates the first matching document with the content of the update document, applying the specified options
Document result = collection.findOneAndUpdate(filter, update, options);
// Prints the matched document in its state before the operation
System.out.println(result.toJson());

A saída do código anterior deve ficar assim:

{"food": "pizza", "color": "green"}

Para obter mais informações sobre a classe Projections , consulte nosso guia no construtor de projeções.

Para obter mais informações sobre a operação upsert, consulte nosso guia sobre upserts.

Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:

Para localizar e substituir um documento, use o método findOneAndReplace() da classe MongoCollection . O método findOneAndReplace() retorna o documento encontrado ou null se nenhum documento corresponder à sua query.

O exemplo a seguir usa o método findOneAndReplace() para localizar um documento com o campo color definido como "green" e substituí-lo pelo seguinte documento:

{"music": "classical", "color": "green"}

O exemplo também usa uma instância FindOneAndReplaceOptions para especificar que o documento retornado deve estar no estado após a nossa operação de substituição.

// <MongoCollection set up code here>
// Creates instructions to replace the matching document with a new document
Bson filter = Filters.eq("color", "green");
Document replace = new Document("music", "classical").append("color", "green");
// Defines options specifying that the operation should return a document in its post-operation state
FindOneAndReplaceOptions options = new FindOneAndReplaceOptions().
returnDocument(ReturnDocument.AFTER);
// Atomically finds and replaces the matching document and prints the replacement document
Document result = collection.findOneAndReplace(filter, replace, options);
System.out.println(result.toJson());

A saída do código anterior deve ficar assim:

{"_id": 1, "music": "classical", "color": "green"}

Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:

Para localizar e excluir um documento, use o método findOneAndDelete() da classe MongoCollection . O método findOneAndDelete() retorna o documento encontrado ou null se nenhum documento corresponder à sua query.

O exemplo a seguir utiliza o método findOneAndDelete() para localizar e excluir o documento com o maior valor no campo _id .

O exemplo utiliza uma instância FindOneAndDeleteOptions para especificar uma classificação decrescente no campo _id .

// <MongoCollection set up code here>
Bson sort = Sorts.descending("_id");
// Creates an empty filter to match all documents in the collection
Bson filter = Filters.empty();
// Defines options that specify a descending sort on the "_id" field
FindOneAndDeleteOptions options = new FindOneAndDeleteOptions().
sort(sort);
// Deletes the document containing the highest "_id" value and prints the deleted document
Document result = collection.findOneAndDelete(filter, options);
System.out.println(result.toJson());

A saída do código anterior deve ficar assim:

{"_id": 2, "food": "pear", "color": "yellow"}

Para obter mais informações sobre a classe Sorts, consulte nosso guia sobre o construtor de classificações.

Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:

Nesta seção, exploraremos dois exemplos. O primeiro exemplo contém uma condição de corrida, o segundo exemplo utiliza uma operação composta para evitar a condição de corrida presente no primeiro exemplo.

Para ambos os exemplos, vamos supor que administramos um hotel com um quarto e que temos um pequeno programa Java para nos ajudar a fazer o checkout desse quarto para um convidado.

O seguinte documento no MongoDB representa a divisão:

{"_id": 1, "guest": null, "room": "Blue Room", "reserved": false}

O código completo para este exemplo está disponível no Github aqui.

Digamos que nosso aplicativo use esse método bookARoom para fazer o checkout do nosso quarto para um convidado:

public void bookARoom() {
// Creates a filter to match documents representing available rooms
Bson filter = Filters.eq("reserved", false);
// Retrieves a document that represents the first available room
Document myRoom = this.collection.find(filter).first();
// Prints a message if no documents representing available rooms are found
if (myRoom == null){
System.out.println("Sorry, we are booked " + this.guest);
return;
}
String myRoomName = myRoom.getString("room");
// Prints a message that guest that successfully booked the room
System.out.println("You got the " + myRoomName + " " + this.guest);
// Creates an update document to mark a room as reserved
Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest));
// Creates a filter that matches the "_id" field of the first available room
Bson roomFilter = Filters.eq("_id", myRoom.get("_id", Integer.class));
// Updates the first matching document to mark it as reserved
this.collection.updateOne(roomFilter, update);
}

Imagine dois convidados separados, Jane e Ana, tentando agendar o quarto com esse método ao mesmo tempo.

Jane vê esta saída:

You got the Blue Room Jan

EPat vê esta saída:

You got the Blue Room Pat

Quando analisamos nosso reconhecimento de data center, vemos o seguinte:

{"_id": 1, "guest": "Jan", "room": "Blue Room", "reserved": true}

Pat ficará chateada. QuandoPapa aparecer em nosso hotel,Jan estará ocupando o quarto dela. O que deu errado?

Aqui está a sequência de eventos que aconteceram da perspectiva de nossa instância do MongoDB:

  • Encontre e devolva um quarto vazio para janeiro

  • Encontre e devolva um quarto vazio paraPat

  • Atualizar o quarto para reservado para Pat

  • Atualize o quarto como reservado para janeiro

Observe que, por um breve momento, Ana reservou a divisão, mas como a operação de atualização de Janeiro foi a última a executar, nosso documento tem "Jan" como convidado.

Vamos usar uma operação composta para evitar a condição de corrida e sempre dar aos nossos usuários a mensagem correta.

public void bookARoom(){
// Creates an update document to mark a room as reserved
Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest));
// Creates a filter to match a document representing an available room
Bson filter = Filters.eq("reserved", false);
// Updates the first document that matches the filter to mark it as reserved
Document myRoom = this.collection.findOneAndUpdate(filter, update);
// Prints a message when there are no available rooms
if (myRoom == null){
System.out.println("Sorry, we are booked " + this.guest);
return;
}
// Prints the name of the guest that successfully booked the room
String myRoomName = myRoom.getString("room");
System.out.println("You got the " + myRoomName + " " + this.guest);
}

Imagine dois convidados separados, Jane e Ana, tentando agendar o quarto com esse método ao mesmo tempo.

Jane vê esta saída:

You got the Blue Room Jan

EPat vê esta saída:

Sorry, we are booked Pat

Quando analisamos nosso reconhecimento de data center, vemos o seguinte:

{"_id":1, "guest":"Jan", "room":"Blue Room", "reserved":true}

Pat recebeu a mensagem correta. Embora possa estar triste por não ter conseguido a reserva, pelo menos sabe que não deve viajar para o nosso hotel.

Aqui está a sequência de eventos que aconteceram da perspectiva de nossa instância do MongoDB:

  • Encontre um quarto vazio para Jane e reserve-o.

  • Tente encontrar um quarto vazio paraPat e reservá-lo. Como não há mais salas, devolva null.

Importante

bloqueio de gravação

Sua instância do MongoDB coloca um bloqueio de escrita no documento que você está modificando durante a operação composta.

Para obter informações sobre a classe Updates , consulte nosso guia sobre o construtor de atualizações.

Para obter mais informações sobre a classe Filters , consulte nosso guia sobre o construtor de filtros.

Para obter mais informações sobre o findOneAndUpdate() método , consulte a documentação da API para a classe MongoCollection.

Voltar

Especificar uma query