Operações compostas
Nesta página
Visão geral
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ção. 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.
Como usar operações compostas
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.
Encontrar e atualizar
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.
Exemplo
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á umMongoExecutionTimeoutException
.
// <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 se assemelha ao seguinte:
{"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:
Encontrar e substituir
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.
Exemplo
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 se assemelha ao seguinte:
{"_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:
Localize e exclua
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.
Exemplo
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 se assemelha ao seguinte:
{"_id": 2, "food": "pear", "color": "yellow"}
Para obter mais informações sobre a classe Sorts
, consulte nosso guia sobre o construtor Sorts.
Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:
Evitando uma condição de corrida
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.
Exemplo com condição de corrida
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.
Exemplo sem condição de corrida
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.