Menu Docs
Página inicial do Docs
/ /

Tutorial: Executar transações ACID multidocumentos

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.

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 collections cart e product e, 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 collections cart e product.

  • models/Cart.java: classe POJO que representa um carro de compras, correspondente a um documento na cart collection.

  • models/Product.java: classe POJO que representa um item e seu estoque, correspondendo a um documento na collection product.

1

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.

2

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.

3

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 cart no banco de dados test.

  • Cria a coleção product no banco de dados test.

  • Aplica um JSON schema à coleção product que define o tipo de dados e as restrições de valor nos campos do documento . Esse esquema garante que o valor do campo stock permaneça acima de 0, 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 .

4

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) }
5

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) }
6

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 cart que representa o carro da Alice

  • Atualize o valor de items.quantity do documento por 2

  • Atualize o documento na coleção product que 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) }
7

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.

8

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 product representando a cervej a.

  • A primeira compra de duas cerejas de Alice, que inclui duas operações: uma para atualizar a coleção cart e outra para atualizar a coleção product. Essas operações não são executadas dentro de uma transação. As operações têm valores Timestamp diferentes, pois não são executadas atomicamente.

  • A próxima compra de duas licenças de Alice, que também atualiza a coleção cart e product. Essas operações têm o mesmo valor Timestamp porque 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.

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.

Voltar

Transações