Visão geral
Neste tutorial, você pode aprender a executar transações de vários documentos que atendem garantias de atomicidade, consistência, isolamento e durabilidade (ACID).
Todas as versões do MongoDB Server oferecem suporte a transações de documento único e garantem a conformidade com ACID quando você executa várias atualizações em um documento em uma transação. No MongoDB Server v4.0 e posterior, você pode executar transações compatíveis com ACID em vários documentos, coleções e bancos de dados.
Tutorial
Este tutorial mostra como baixar um aplicação de exemplo do que executa transações de vários documentos no estoque de produtos e dados de compra. O tutorial utiliza os seguintes arquivos no transactions diretório do aplicativo de exemplo :
Transactions.java: acessa as collectionscarteproducte, em seguida, executa operações em ambas as collections para refletir as compras de bebida. O código opera dentro de uma transação ACID e sem uma transação para comparar as abordagens.ChangeStreams.java: retorna informações sobre alterações de dados nas collectionscarteproduct.models/Cart.java: classe POJO que representa um carro de compras, correspondente a um documento nacartcollection.models/Product.java: classe POJO que representa um item e seu estoque, correspondendo a um documento na collectionproduct.
Verifique os pré-requisitos.
Antes de iniciar este tutorial, verifique se você tem os seguintes componentes preparados:
Conta do MongoDB Atlas com um cluster configurado. Para saber como criar um cluster, consulte o guia de Introdução ao MongoDB .
Java driver v5.0 ou posterior.
Java 21 ou posterior.
Maven v3.8.7 ou posterior.
Baixe o aplicação de amostra .
Clone o aplicação de amostra do repositório GitHub do MongoDB Developer executando o seguinte comando em seu terminal:
git clone git@github.com:mongodb-developer/java-quick-start.git
Este repositório contém uma pasta transactions que armazena os arquivos para este tutorial.
Inicie um change stream e configure collections.
Para criar as cart product collections e e configurar um JSON schema, execute o arquivo ChangeStreams.java. Navegue até o java-quick-start diretório e execute o seguinte comando:
mvn compile exec:java \ -Dexec.mainClass="com.mongodb.quickstart.transactions.ChangeStreams" \ -Dmongodb.uri="<connection URI>"
Dica
Substitua o espaço reservado <connection URI> pelo URI de conexão do seu cluster.
Este arquivo executa as seguintes ações:
Cria a coleção
cartno banco de dadostest.Cria a coleção
productno banco de dadostest.Aplica um JSON schema à coleção
productque define o tipo de dados e as restrições de valor nos campos do documento . Esse esquema garante que o valor do campostockpermaneça acima de0, portanto, qualquer transação que tente comprar um item fora de estoque apresente um erro e não seja bem-sucedida.Abre um change stream para monitorar as alterações no banco de dados
test.
Inicie as operações de dados inserindo dados de amostra.
Alice é uma cliente que quer comprar cervej a . O arquivo Transactions.java executa operações de banco de dados para refletir suas compras. Para iniciar o programa, abra uma segunda janela do terminal e execute o seguinte código no diretório raiz do projeto:
mvn compile exec:java \ -Dexec.mainClass="com.mongodb.quickstart.transactions.Transactions" \ -Dmongodb.uri="<connection URI>"
Dica
Substitua o espaço reservado <connection URI> pelo URI de conexão do seu cluster.
O arquivo insere um documento na coleção product que representa o inventário de variedades e define seu valor stock como 5. Este documento armazena os seguintes dados:
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(5) }
Execute as primeiras operações sem uma transação.
Depois de inserir dados de amostra, o arquivo Transactions.java executa a primeira operação de atualização para representar a compra de duas cervej as de Alice. O código chama os métodos aliceWantsTwoBeers() e removingBeersFromStock() sem iniciar uma transação. Esses métodos têm as seguintes definições:
private static void aliceWantsTwoBeers() { System.out.println("Alice adds 2 beers in her cart."); cartCollection.insertOne(new Cart("Alice", List.of(new Cart.Item(BEER_ID, 2, BEER_PRICE)))); }
private static void removingBeersFromStock() { System.out.println("Trying to update beer stock : -2 beers."); try { productCollection.updateOne(filterId, decrementTwoBeers); } catch (MongoException e) { System.out.println("######## MongoException ########"); System.out.println("##### STOCK CANNOT BE NEGATIVE #####"); throw e; } }
O método aliceWantsTwoBeers() adiciona duas variedades de ao Car de compras da Alice inserindo um documento na coleção cart que representa a compra. Em seguida, o método removingBeersFromStock() atualiza a coleção product para refletir as alterações e diminuir o número de variedades em stock.
Selecione a aba Cart para ver o novo documento cart que representa o carro de compras da Alice e selecione a aba Product para ver o documento product que representa o inventário de variedades após as operações:
{ "_id" : "Alice", "items" : [ { "price" : NumberDecimal("3"), "productId" : "beer", "quantity" : NumberInt(2) } ] }
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(3) }
Execute uma segunda operação em uma transação multidocumento.
Após a compra inicial, Alice adiciona mais duas cervej as ao car . O arquivo Transactions.java utiliza uma transação para executar esta segunda operação chamando o método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() e passando um MongoClient como um argumento. Este método tem a seguinte definição:
private static void aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback(MongoClient client) { ClientSession session = client.startSession(); try { session.startTransaction(TransactionOptions.builder().writeConcern(WriteConcern.MAJORITY).build()); aliceWantsTwoExtraBeers(session); sleep(); removingBeerFromStock(session); session.commitTransaction(); } catch (MongoException e) { session.abortTransaction(); System.out.println("####### ROLLBACK TRANSACTION #######"); } finally { session.close(); System.out.println("####################################\n"); printDatabaseState(); } }
O método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() inicia uma sessão e, em seguida, inicia uma transação. Na transação, o código chama métodos assistente para executar as seguintes ações:
Encontre o documento na collection
cartque representa o carro da AliceAtualize o valor de
items.quantitydo documento por2Atualize o documento na coleção
productque representa o estoque de cadeia de caracteres para refletir a alteração
Como são executadas em uma transação ACID de vários documentos, as atualizações cart e product são atômicas.
Selecione a aba para Cart ver cart Product product o documento atualizado que representa o documento
{ "_id" : "Alice", "items" : [ { "price" : NumberDecimal("3"), "productId" : "beer", "quantity" : NumberInt(4) } ] }
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(1) }
Execute uma operação malsucedida em uma transação.
Finalmente, Alice tenta adicionar mais duas cervej as ao car . O arquivo Transactions.java usa uma transação para executar essa terceira operação chamando o método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() novamente.
No entanto, esta operação não é bem-sucedida porque só há uma cervej a restante em estoque. O JSON schema configurado no arquivo ChangeStreams.java garante que o valor stock da coleção product não possa estar abaixo de 0, portanto, a tentativa de subtrair 2 de seu valor atual gera um erro. O método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() reverte a transação.
Revise a saída do fluxo de alterações.
Após o término da execução do Transactions.java, o resultado do arquivo ChangeStreams.java será semelhante ao seguinte:
Dropping the 'test' database. Creating the 'cart' collection. Creating the 'product' collection with a JSON Schema. Watching the collections in the DB test... Timestamp{value=7304460075832180737, seconds=1700702141, inc=1} => Document{{_id=beer, price=3, stock=5}} Timestamp{value=7304460075832180738, seconds=1700702141, inc=2} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=2}}]}} Timestamp{value=7304460080127148033, seconds=1700702142, inc=1} => Document{{_id=beer, price=3, stock=3}} Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=4}}]}} Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=beer, price=3, stock=1}}
O change stream imprime informações sobre a configuração da collection e as seguintes operações:
A operação de inserção, que adiciona um documento à coleção
productrepresentando a cervej a.A primeira compra de duas cerejas de Alice, que inclui duas operações: uma para atualizar a coleção
carte outra para atualizar a coleçãoproduct. Essas operações não são executadas dentro de uma transação. As operações têm valoresTimestampdiferentes, pois não são executadas atomicamente.A próxima compra de duas licenças de Alice, que também atualiza a coleção
carteproduct. Essas operações têm o mesmo valorTimestampporque são executadas atomicamente em uma transação de vários documentos.
Após concluir este tutorial, você tem um aplicação que atualiza os dados de gerenciamento de estoque. O aplicação executa essas operações de atualização com e sem uma transação ACID de vários documentos para comparar os dois resultados.
Informações adicionais
Para visualizar o aplicação de exemplo completo, consulte a pasta de transações no java-quick-start repositório do GitHub.
Para saber mais sobre transações, consulte o guia Transações.