Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
Javachevron-right

Transações ACID multidocumento Java - MongoDB

Maxime Beugnet10 min read • Published Aug 15, 2018 • Updated Mar 01, 2024
MongoDBJava
SNIPPET
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Início rápido
star-empty
star-empty
star-empty
star-empty
star-empty

Introdução

Introduzidas em junho de 2018 com o MongoDB 4.0, agora são permitidas transações ACID de vários documentos.
Mas espera... Isso significa que o MongoDB não suportava transações antes disso? Não, o MongoDB sempre foi compatível com transação, inicialmente na forma de transação de documento único.
O MongoDB 4.0 estende essas garantias transacionais a vários documentos, declarações, coleções e bancos de dados. De que adiantaria um banco de dados sem nenhuma forma de garantia de integridade de dados transacionais?
Antes de se aprofundar nos detalhes, você pode acessar o código e testar transações ACID multi-documento.
1git clone git@github.com:mongodb-developer/java-quick-start.git

Início rápido

Última atualização: 28 de fevereiro de 2024

  • Atualizar para o Java 21
  • Atualize o driver Java para 5.0.0
  • Atualize logback-classic para 1.2.13

Requisitos

  • Java 21
  • Maven 3.8.7
  • Docker (opcional)

Etapa 1: iniciar o MongoDB

Comece a usar o MongoDB Atlas e obtenha um cluster gratuito.
Ou você pode iniciar um conjunto de réplicas de nó único efêmero usando o Docker para testar rapidamente:
1docker run --rm -d -p 27017:27017 -h $(hostname) --name mongo mongo:7.0.5 --replSet=RS && sleep 3 && docker exec mongo mongosh --quiet --eval "rs.initiate();"

Etapa 2: iniciar o Java

Esta demonstração contém dois programas principais: ChangeStreams.java e Transactions.java.
  • A classe ChangeSteams permite receber notificações de quaisquer alterações de dados dentro das duas coleções usadas neste tutorial.
  • A classe Transactions é a própria demonstração.
Você precisa de dois shells para executá-los.
Primeiro shell:
1mvn compile exec:java -Dexec.mainClass="com.mongodb.quickstart.transactions.ChangeStreams" -Dmongodb.uri="mongodb+srv://USERNAME:PASSWORD@cluster0-abcde.mongodb.net/test?w=majority"
Segundo shell:
1mvn compile exec:java -Dexec.mainClass="com.mongodb.quickstart.transactions.Transactions" -Dmongodb.uri="mongodb+srv://USERNAME:PASSWORD@cluster0-abcde.mongodb.net/test?w=majority"
Observação: sempre execute o programa ChangeStreams primeiro porque ele cria a coleção product com o JSON schema necessário.
Vamos comparar nossas transações de documento único existentes com as transações multidocumento do MongoDB 4.0 compatíveis com ACID e ver como podemos aproveitar esse novo recurso com Java.

Antes do MongoDB 4.0

Mesmo no MongoDB 3.6 e anteriores, cada operação de gravação é representada como uma transação com escopo no nível de um documento individual na camada de armazenamento. Como o document model reúne dados relacionados que, de outra forma, seriam modelados em tabelas pai-filho separadas em um esquema tabular, as operações atômicas de documento único do MongoDB fornecem uma semântica de transação que atende às necessidades de integridade de dados da maioria dos aplicativos.
Cada operação de escrita típica que modifica vários documentos acontece em várias transações independentes: uma para cada documento.
Vejamos um exemplo com um aplicativo de gerenciamento de estoque muito simples.
Antes de mais nada, preciso de um conjunto de réplicas do MongoDB, então siga as instruções fornecidas acima para iniciar o MongoDB.
Agora, vamos inserir os seguintes documentos em uma coleção product:
1db.product.insertMany([
2 { "_id" : "beer", "price" : NumberDecimal("3.75"), "stock" : NumberInt(5) },
3 { "_id" : "wine", "price" : NumberDecimal("7.5"), "stock" : NumberInt(3) }
4])
Vamos imaginar que há uma promoção, e queremos oferecer aos nossos clientes um desconto de 20% em todos os nossos produtos.
Mas antes de aplicar esse desconto, queremos monitorar quando essas operações estão ocorrendo no MongoDB com o Change Streams.
Execute o seguinte em um shell do MongoDB:
1cursor = db.product.watch([{$match: {operationType: "update"}}]);
2while (!cursor.isClosed()) {
3 let next = cursor.tryNext()
4 while (next !== null) {
5 printjson(next);
6 next = cursor.tryNext()
7 }
8}
Reserva este shell, abra outro shell do MongoDB e aplique o desconto:
1RS [direct: primary] test> db.product.updateMany({}, {$mul: {price:0.8}})
2{
3 acknowledged: true,
4 insertedId: null,
5 matchedCount: 2,
6 modifiedCount: 2,
7 upsertedCount: 0
8}
9RS [direct: primary] test> db.product.find().pretty()
10[
11 { _id: 'beer', price: Decimal128("3.00000000000000000"), stock: 5 },
12 { _id: 'wine', price: Decimal128("6.0000000000000000"), stock: 3 }
13]
Como você pode ver, ambos os documentos foram atualizados com uma única linha de comando, mas não em uma única transação. Veja o que podemos ver no shell do fluxo de alterações:
1{
2 _id: {
3 _data: '8265580539000000012B042C0100296E5A1004A7F55A5B35BD4C7DB2CD56C6CFEA9C49463C6F7065726174696F6E54797065003C7570646174650046646F63756D656E744B657900463C5F6964003C6265657200000004'
4 },
5 operationType: 'update',
6 clusterTime: Timestamp({ t: 1700267321, i: 1 }),
7 wallTime: ISODate("2023-11-18T00:28:41.601Z"),
8 ns: {
9 db: 'test',
10 coll: 'product'
11 },
12 documentKey: {
13 _id: 'beer'
14 },
15 updateDescription: {
16 updatedFields: {
17 price: Decimal128("3.00000000000000000")
18 },
19 removedFields: [],
20 truncatedArrays: []
21 }
22}
23{
24 _id: {
25 _data: '8265580539000000022B042C0100296E5A1004A7F55A5B35BD4C7DB2CD56C6CFEA9C49463C6F7065726174696F6E54797065003C7570646174650046646F63756D656E744B657900463C5F6964003C77696E6500000004'
26 },
27 operationType: 'update',
28 clusterTime: Timestamp({ t: 1700267321, i: 2 }),
29 wallTime: ISODate("2023-11-18T00:28:41.601Z"),
30 ns: {
31 db: 'test',
32 coll: 'product'
33 },
34 documentKey: {
35 _id: 'wine'
36 },
37 updateDescription: {
38 updatedFields: {
39 price: Decimal128("6.0000000000000000")
40 },
41 removedFields: [],
42 truncatedArrays: []
43 }
44}
Como você pode ver, os tempos de cluster (veja a chave clusterTime) das duas operações são diferentes: as operações ocorreram durante o mesmo segundo, mas o contador do carimbo de data/hora foi incrementado em um.
Portanto, aqui cada documento é atualizado um de cada vez e, mesmo que isso aconteça muito rapidamente, outra pessoa poderia ler os documentos enquanto a atualização está sendo executada e ver apenas um dos dois produtos com desconto.
Na maioria das vezes, isso é algo que você pode tolerar em seu MongoDB database porque, tanto quanto possível, tentamos incorporar dados bem vinculados (ou relacionados) no mesmo documento.
Consequentemente, duas atualizações no mesmo documento ocorrem em uma única transação:
1RS [direct: primary] test> db.product.updateOne({_id: "wine"},{$inc: {stock:1}, $set: {description : "It's the best wine on Earth"}})
2{
3 acknowledged: true,
4 insertedId: null,
5 matchedCount: 1,
6 modifiedCount: 1,
7 upsertedCount: 0
8}
9RS [direct: primary] test> db.product.findOne({_id: "wine"})
10{
11 _id: 'wine',
12 price: Decimal128("6.0000000000000000"),
13 stock: 4,
14 description: 'It's the best wine on Earth'
15}
No entanto, às vezes, não é possível modelar todos os dados relacionados em um único documento, e há muitos motivos válidos para optar por não incorporar documentos.

MongoDB 4.0 com transações ACID multidocumento

As transações ACID multidocumento no MongoDB se assemelham muito ao que você talvez já conheça com os bancos de dados relacionais tradicionais.
As transação do MongoDB são um conjunto conversacional de operações relacionadas que devem ser confirmadas atomicamente ou totalmente revertidas com a execução de tudo ou nada.
As transação são usadas para garantir que as operações sejam atômicas, mesmo em várias coleções ou banco de dados. Consequentemente, com leituras de isolamento de snapshot, outro usuário só pode observar todas as operações ou nenhuma delas.
Vamos agora adicionar um carrinho de compras ao nosso exemplo.
Para este exemplo, duas coleções são necessárias porque estamos lidando com duas entidades comerciais diferentes: a gestão do estoque e o carrinho de compras que cada cliente pode criar durante as compras. Os ciclos de vida de cada documento nessas coleções são diferentes.
Um documento na coleção de produtos representa um item que estou vendo. Contém o preço atual do produto e o estoque atual. Criei um POJO para representá-lo: User.java.
1{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(5) }
Um carrinho de compras é criado quando um cliente adiciona seu primeiro item ao carrinho e é removido quando o cliente finaliza a compra ou sai do site. Criei um POJO para representá-lo: Cart.java.
1{
2 "_id" : "Alice",
3 "items" : [
4 {
5 "price" : NumberDecimal("3"),
6 "productId" : "beer",
7 "quantity" : NumberInt(2)
8 }
9 ]
10}
O desafio aqui reside no fato de que não posso vender mais do que possuo: se tenho cinco cervejas para vender, não posso ter mais de cinco cervejas distribuídas pelos diferentes carrinhos de clientes.
Para assegurar isso, preciso garantir que a operação que cria ou atualiza o carrinho do cliente seja atômica com a atualização do estoque. É aqui que a transação multidocumento entra em ação. A transação deve falhar caso alguém tente comprar algo que eu não tenha em meu estoque. Adicionarei uma restrição ao estoque do produto:
1db.product.drop()
2db.createCollection("product", {
3 validator: {
4 $jsonSchema: {
5 bsonType: "object",
6 required: [ "_id", "price", "stock" ],
7 properties: {
8 _id: {
9 bsonType: "string",
10 description: "must be a string and is required"
11 },
12 price: {
13 bsonType: "decimal",
14 minimum: 0,
15 description: "must be a non-negative decimal and is required"
16 },
17 stock: {
18 bsonType: "int",
19 minimum: 0,
20 description: "must be a non-negative integer and is required"
21 }
22 }
23 }
24 }
25})
Observe que isso já está incluído no código Java da classe ChangeStreams.
Para monitorar nosso exemplo, usaremos o Change Streams do MongoDB que foi introduzido no MongoDB 3.6.
Em ChangeStreams.java, vamos monitorar o banco de dados test que contém nossas duas coleções. Ele imprime cada operação com o tempo de cluster associado.
1package com.mongodb.quickstart.transactions;
2
3import com.mongodb.ConnectionString;
4import com.mongodb.MongoClientSettings;
5import com.mongodb.client.MongoClient;
6import com.mongodb.client.MongoClients;
7import com.mongodb.client.MongoDatabase;
8import com.mongodb.client.model.CreateCollectionOptions;
9import com.mongodb.client.model.ValidationAction;
10import com.mongodb.client.model.ValidationOptions;
11import org.bson.BsonDocument;
12
13import static com.mongodb.client.model.changestream.FullDocument.UPDATE_LOOKUP;
14
15public class ChangeStreams {
16
17 private static final String CART = "cart";
18 private static final String PRODUCT = "product";
19
20 public static void main(String[] args) {
21 ConnectionString connectionString = new ConnectionString(System.getProperty("mongodb.uri"));
22 MongoClientSettings clientSettings = MongoClientSettings.builder()
23 .applyConnectionString(connectionString)
24 .build();
25 try (MongoClient client = MongoClients.create(clientSettings)) {
26 MongoDatabase db = client.getDatabase("test");
27 System.out.println("Dropping the '" + db.getName() + "' database.");
28 db.drop();
29 System.out.println("Creating the '" + CART + "' collection.");
30 db.createCollection(CART);
31 System.out.println("Creating the '" + PRODUCT + "' collection with a JSON Schema.");
32 db.createCollection(PRODUCT, productJsonSchemaValidator());
33 System.out.println("Watching the collections in the DB " + db.getName() + "...");
34 db.watch()
35 .fullDocument(UPDATE_LOOKUP)
36 .forEach(doc -> System.out.println(doc.getClusterTime() + " => " + doc.getFullDocument()));
37 }
38 }
39
40 private static CreateCollectionOptions productJsonSchemaValidator() {
41 String jsonSchema = """
42 {
43 "$jsonSchema": {
44 "bsonType": "object",
45 "required": ["_id", "price", "stock"],
46 "properties": {
47 "_id": {
48 "bsonType": "string",
49 "description": "must be a string and is required"
50 },
51 "price": {
52 "bsonType": "decimal",
53 "minimum": 0,
54 "description": "must be a non-negative decimal and is required"
55 },
56 "stock": {
57 "bsonType": "int",
58 "minimum": 0,
59 "description": "must be a non-negative integer and is required"
60 }
61 }
62 }
63 }""";
64 return new CreateCollectionOptions().validationOptions(
65 new ValidationOptions().validationAction(ValidationAction.ERROR)
66 .validator(BsonDocument.parse(jsonSchema)));
67 }
68}
Neste exemplo, temos cinco cervejas para vender.
Alice quer comprar duas cervejas, mas não usaremos uma transação com vários documentos para isso. Observaremos nos fluxos de alterações duas operações em dois momentos diferentes do cluster:
  • Um criando o carrinho
  • Um atualizando o estoque
Alice depois adiciona mais duas cervejas ao carrinho, e vamos usar uma transação desta vez. O resultado no fluxo de alterações serão duas operações acontecendo ao mesmo tempo do cluster.
Por fim, ela tentará pedir duas cervejas extras, mas o validador jsonSchema falhará na atualização do produto (pois há apenas uma em estoque) e resultará em uma reversão. Não veremos nada no fluxo de alterações. Abaixo está o código fonte para Transaction.java:
1package com.mongodb.quickstart.transactions;
2
3import com.mongodb.*;
4import com.mongodb.client.*;
5import com.mongodb.quickstart.transactions.models.Cart;
6import com.mongodb.quickstart.transactions.models.Product;
7import org.bson.BsonDocument;
8import org.bson.codecs.configuration.CodecRegistry;
9import org.bson.codecs.pojo.PojoCodecProvider;
10import org.bson.conversions.Bson;
11
12import java.math.BigDecimal;
13import java.util.ArrayList;
14import java.util.Collections;
15import java.util.List;
16
17import static com.mongodb.client.model.Filters.*;
18import static com.mongodb.client.model.Updates.inc;
19import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
20import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
21
22public class Transactions {
23
24 private static final BigDecimal BEER_PRICE = BigDecimal.valueOf(3);
25 private static final String BEER_ID = "beer";
26 private static final Bson filterId = eq("_id", BEER_ID);
27 private static final Bson filterAlice = eq("_id", "Alice");
28 private static final Bson matchBeer = elemMatch("items", eq("productId", "beer"));
29 private static final Bson incrementTwoBeers = inc("items.$.quantity", 2);
30 private static final Bson decrementTwoBeers = inc("stock", -2);
31 private static MongoCollection<Cart> cartCollection;
32 private static MongoCollection<Product> productCollection;
33
34 public static void main(String[] args) {
35 ConnectionString connectionString = new ConnectionString(System.getProperty("mongodb.uri"));
36 CodecRegistry pojoCodecRegistry = fromProviders(PojoCodecProvider.builder().automatic(true).build());
37 CodecRegistry codecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), pojoCodecRegistry);
38 MongoClientSettings clientSettings = MongoClientSettings.builder()
39 .applyConnectionString(connectionString)
40 .codecRegistry(codecRegistry)
41 .build();
42 try (MongoClient client = MongoClients.create(clientSettings)) {
43 MongoDatabase db = client.getDatabase("test");
44 cartCollection = db.getCollection("cart", Cart.class);
45 productCollection = db.getCollection("product", Product.class);
46 transactionsDemo(client);
47 }
48 }
49
50 private static void transactionsDemo(MongoClient client) {
51 clearCollections();
52 insertProductBeer();
53 printDatabaseState();
54 System.out.println("""
55 ######### NO TRANSACTION #########
56 Alice wants 2 beers.
57 We have to create a cart in the 'cart' collection and update the stock in the 'product' collection.
58 The 2 actions are correlated but can not be executed at the same cluster time.
59 Any error blocking one operation could result in stock error or a sale of beer that we can't fulfill as we have no stock.
60 ------------------------------------""");
61 aliceWantsTwoBeers();
62 sleep();
63 removingBeersFromStock();
64 System.out.println("####################################\n");
65 printDatabaseState();
66 sleep();
67 System.out.println("""
68 ######### WITH TRANSACTION #########
69 Alice wants 2 extra beers.
70 Now we can update the 2 collections simultaneously.
71 The 2 operations only happen when the transaction is committed.
72 ------------------------------------""");
73 aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback(client);
74 sleep();
75 System.out.println("""
76 ######### WITH TRANSACTION #########
77 Alice wants 2 extra beers.
78 This time we do not have enough beers in stock so the transaction will rollback.
79 ------------------------------------""");
80 aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback(client);
81 }
82
83 private static void aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback(MongoClient client) {
84 ClientSession session = client.startSession();
85 try {
86 session.startTransaction(TransactionOptions.builder().writeConcern(WriteConcern.MAJORITY).build());
87 aliceWantsTwoExtraBeers(session);
88 sleep();
89 removingBeerFromStock(session);
90 session.commitTransaction();
91 } catch (MongoException e) {
92 session.abortTransaction();
93 System.out.println("####### ROLLBACK TRANSACTION #######");
94 } finally {
95 session.close();
96 System.out.println("####################################\n");
97 printDatabaseState();
98 }
99 }
100
101 private static void removingBeersFromStock() {
102 System.out.println("Trying to update beer stock : -2 beers.");
103 try {
104 productCollection.updateOne(filterId, decrementTwoBeers);
105 } catch (MongoException e) {
106 System.out.println("######## MongoException ########");
107 System.out.println("##### STOCK CANNOT BE NEGATIVE #####");
108 throw e;
109 }
110 }
111
112 private static void removingBeerFromStock(ClientSession session) {
113 System.out.println("Trying to update beer stock : -2 beers.");
114 try {
115 productCollection.updateOne(session, filterId, decrementTwoBeers);
116 } catch (MongoException e) {
117 System.out.println("######## MongoException ########");
118 System.out.println("##### STOCK CANNOT BE NEGATIVE #####");
119 throw e;
120 }
121 }
122
123 private static void aliceWantsTwoBeers() {
124 System.out.println("Alice adds 2 beers in her cart.");
125 cartCollection.insertOne(new Cart("Alice", List.of(new Cart.Item(BEER_ID, 2, BEER_PRICE))));
126 }
127
128 private static void aliceWantsTwoExtraBeers(ClientSession session) {
129 System.out.println("Updating Alice cart : adding 2 beers.");
130 cartCollection.updateOne(session, and(filterAlice, matchBeer), incrementTwoBeers);
131 }
132
133 private static void insertProductBeer() {
134 productCollection.insertOne(new Product(BEER_ID, 5, BEER_PRICE));
135 }
136
137 private static void clearCollections() {
138 productCollection.deleteMany(new BsonDocument());
139 cartCollection.deleteMany(new BsonDocument());
140 }
141
142 private static void printDatabaseState() {
143 System.out.println("Database state:");
144 printProducts(productCollection.find().into(new ArrayList<>()));
145 printCarts(cartCollection.find().into(new ArrayList<>()));
146 System.out.println();
147 }
148
149 private static void printProducts(List<Product> products) {
150 products.forEach(System.out::println);
151 }
152
153 private static void printCarts(List<Cart> carts) {
154 if (carts.isEmpty()) {
155 System.out.println("No carts...");
156 } else {
157 carts.forEach(System.out::println);
158 }
159 }
160
161 private static void sleep() {
162 System.out.println("Sleeping 1 second...");
163 try {
164 Thread.sleep(1000);
165 } catch (InterruptedException e) {
166 System.err.println("Oops!");
167 e.printStackTrace();
168 }
169 }
170}
Aqui está o console do fluxo de alterações:
1Dropping the 'test' database.
2Creating the 'cart' collection.
3Creating the 'product' collection with a JSON Schema.
4Watching the collections in the DB test...
5Timestamp{value=7304460075832180737, seconds=1700702141, inc=1} => Document{{_id=beer, price=3, stock=5}}
6Timestamp{value=7304460075832180738, seconds=1700702141, inc=2} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=2}}]}}
7Timestamp{value=7304460080127148033, seconds=1700702142, inc=1} => Document{{_id=beer, price=3, stock=3}}
8Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=4}}]}}
9Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=beer, price=3, stock=1}}
Como você pode ver aqui, obtemos apenas cinco operações porque as duas últimas operações nunca foram confirmadas no banco de dados e, portanto, o fluxo de alterações não tem nada a mostrar.
  • A primeira operação é a inicialização da coleção de produtos (criar o documento do produto para as cervejas).
  • A segunda e a terceira operações são as duas primeiras cervejas que Alice adiciona ao carrinho sem uma transação com vários documentos. Observe que as duas operações não ocorrem no mesmo tempo do cluster.
  • As duas últimas operações são as duas cervejas adicionais que Alice adiciona ao carrinho com uma transação multi-doc. Observe que desta vez as duas operações são atômicas e acontecem exatamente no mesmo horário do cluster.
Este é o console do processo Java de transação que resume tudo o que eu disse anteriormente.
1Database state:
2Product{id='beer', stock=5, price=3}
3No carts...
4
5######### NO TRANSACTION #########
6Alice wants 2 beers.
7We have to create a cart in the 'cart' collection and update the stock in the 'product' collection.
8The 2 actions are correlated but can not be executed on the same cluster time.
9Any error blocking one operation could result in stock error or a sale of beer that we can't fulfill as we have no stock.
10------------------------------------
11Alice adds 2 beers in her cart.
12Sleeping 1 second...
13Trying to update beer stock : -2 beers.
14####################################
15
16Database state:
17Product{id='beer', stock=3, price=3}
18Cart{id='Alice', items=[Item{productId=beer, quantity=2, price=3}]}
19
20Sleeping 1 second...
21######### WITH TRANSACTION #########
22Alice wants 2 extra beers.
23Now we can update the 2 collections simultaneously.
24The 2 operations only happen when the transaction is committed.
25------------------------------------
26Updating Alice cart : adding 2 beers.
27Sleeping 1 second...
28Trying to update beer stock : -2 beers.
29####################################
30
31Database state:
32Product{id='beer', stock=1, price=3}
33Cart{id='Alice', items=[Item{productId=beer, quantity=4, price=3}]}
34
35Sleeping 1 second...
36######### WITH TRANSACTION #########
37Alice wants 2 extra beers.
38This time we do not have enough beers in stock so the transaction will rollback.
39------------------------------------
40Updating Alice cart : adding 2 beers.
41Sleeping 1 second...
42Trying to update beer stock : -2 beers.
43######## MongoException ########
44##### STOCK CANNOT BE NEGATIVE #####
45####### ROLLBACK TRANSACTION #######
46####################################
47
48Database state:
49Product{id='beer', stock=1, price=3}
50Cart{id='Alice', items=[Item{productId=beer, quantity=4, price=3}]}

Próximos passos

Obrigado por ler minha postagem. Espero que seja útil e interessante para você. Lembre-se de que todo o código está disponível no repositório do GitHub para você experimentar.
Se estiver procurando uma maneira fácil de começar a usar o MongoDB, você poderá fazer isso em apenas cinco cliques usando nosso serviço de banco de dados em nuvem do MongoDB Atlas.

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Início rápido
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Implementação de gravações em massa usando o Spring Boot para MongoDB


Mar 22, 2023 | 3 min read
Podcast

Expansão do setor de jogos com Gaspard Petit, da Square Enix


Mar 22, 2023 | 29 min
Início rápido

Java - Mapeamento de POJOs


Mar 01, 2024 | 5 min read
Tutorial

Java encontra Queryable Encryption: desenvolvendo um aplicativo de conta bancária seguro


Oct 08, 2024 | 14 min read
Sumário