As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Execução de operações CRUD no Amazon DocumentDB com Java
Esta seção discute a execução da operação CRUD (criar, ler, atualizar, excluir) no Amazon DocumentDB usando drivers de Java do MongoDB.
Tópicos
Criação e inserção de documentos em uma coleção do DocumentDB
A inserção de documentos no Amazon DocumentDB permite que você adicione novos dados às suas coleções. Há várias maneiras de realizar inserções, dependendo das suas necessidades e do volume de dados com os quais você esteja trabalhando. O método mais básico para inserir um documento individual na coleção é insertOne(). Para inserir vários documentos ao mesmo tempo, é possível usar o método insertMany(), que permite adicionar uma matriz de documentos em uma única operação. Outro método para inserir muitos documentos em uma coleção do DocumentDB é o bulkWrite(). Neste guia, discutimos todos esses métodos para criar documentos em uma coleção do DocumentDB.
insertOne()
Vamos começar examinando como inserir um documento individual em uma coleção do Amazon DocumentDB. A inserção de um único documento é realizada usando o método insertOne(). Esse método usa a BsonDocumentInsertOneResult
Document article = new Document() .append("restaurantId", "REST-21G145") .append("name", "Future-proofed Intelligent Bronze Hat") .append("cuisine", "International") .append("rating", new Document() .append("average", 1.8) .append("totalReviews", 267)) .append("features", Arrays.asList("Outdoor Seating", "Live Music")); try { InsertOneResult result = collection.insertOne(article); System.out.println("Inserted document with the following id: " + result.getInsertedId()); } catch (MongoWriteException e) { // Handle duplicate key or other write errors System.err.println("Failed to insert document: " + e.getMessage()); throw e; } catch (MongoException e) { // Handle other MongoDB errors System.err.println("MongoDB error: " + e.getMessage()); throw e; }
Ao usar insertOne(), certifique-se de incluir o tratamento adequado de erros. Por exemplo, no código acima, “restaurantId” tem um índice exclusivo e, portanto, executar esse código novamente gerará a MongoWriteException a seguir:
Failed to insert document: Write operation error on server docdbCluster.docdb.amazonaws.com:27017.
Write error: WriteError{code=11000, message='E11000 duplicate key error collection: Restaurants index: restaurantId_1', details={}}.
insertMany()
Os principais métodos usados para inserir muitos documentos em uma coleção são insertMany() e bulkWrite().
O método insertMany() é a maneira mais simples de inserir vários documentos em uma única operação. Ele aceita uma lista de documentos e os insere na coleção. Esse método é ideal quando você insere um lote de novos documentos que são independentes uns dos outros e não exigem nenhum processamento especial ou operações mistas. O código a seguir mostra a leitura de documentos JSON de um arquivo e a inserção deles na coleção. A insertMany() função retorna um InsertManyResultInsertManyResultobjeto que pode ser usado para obter todos os documentos inseridos. IDs
// Read JSON file content String content = new String(Files.readAllBytes(Paths.get(jsonFileName))); JSONArray jsonArray = new JSONArray(content); // Convert JSON articles to Documents List < Document > restaurants = new ArrayList < > (); for (int i = 0; i < jsonArray.length(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); Document doc = Document.parse(jsonObject.toString()); restaurants.add(doc); } //insert documents in collection InsertManyResult result = collection.insertMany(restaurants); System.out.println("Count of inserted documents: " + result.getInsertedIds().size());
O método bulkWrite() permite realizar várias operações de gravação (inserir, atualizar, excluir) em um único lote. É possível usar bulkWrite() quando precisar realizar diferentes tipos de operações em um único lote, como inserir alguns documentos e atualizar outros. bulkWrite() oferece suporte a dois tipos de gravação em lote, ordenada e não ordenada:
Operações ordenadas: (padrão) o Amazon DocumentDB processa as operações de gravação sequencialmente e para no primeiro erro encontrado. Isso é útil quando a ordem das operações é importante, como quando as operações posteriores dependem das anteriores. No entanto, as operações ordenadas geralmente são mais lentas do que as operações não ordenadas. Com as operações ordenadas, você deve resolver o caso em que o lote é interrompido no primeiro erro, potencialmente deixando algumas operações sem processamento.
Operações não ordenadas: permite que o Amazon DocumentDB processe inserções como uma única execução no banco de dados. Se ocorrer um erro com um documento, a operação continuará com os documentos restantes. Isso é particularmente útil quando você insere grandes quantidades de dados e pode tolerar algumas falhas, como durante a migração de dados ou importações em massa, em que alguns documentos podem falhar devido a chaves duplicadas. Com operações não ordenadas, você deve abordar cenários de sucesso parcial em que algumas operações obtêm êxito e outras falham.
Ao trabalhar com o método bulkWrite(), algumas classes essenciais são necessárias. Primeiro, a classe WriteModelInsertOneModelUpdateOneModelUpdateManyModelDeleteOneModelDeleteManyModel
A BulkWriteOptionsBulkWriteResult
Para o tratamento de erros, a classe MongoBulkWriteExceptionBulkWriteErrorbulkWrite(). O código também mostra como trabalhar com BulkWriteOptionsBulkWriteResultbulkWrite().
List < WriteModel < Document >> bulkOperations = new ArrayList < > (); // get list of 10 documents representing 10 restaurants List < Document > restaurantsToInsert = getSampleData(); for (Document doc: restaurantsToInsert) { bulkOperations.add(new InsertOneModel < > (doc)); } // Update operation bulkOperations.add(new UpdateOneModel < > ( new Document("restaurantId", "REST-Y2E9H5"), new Document("", new Document("stats.likes", 20)) .append("", new Document("rating.average", 4.5)))); // Delete operation bulkOperations.add(new DeleteOneModel < > (new Document("restaurantId", "REST-D2L431"))); // Perform bulkWrite operation try { BulkWriteOptions options = new BulkWriteOptions() .ordered(false); // Allow unordered inserts BulkWriteResult result = collection.bulkWrite(bulkOperations, options); System.out.println("Inserted: " + result.getInsertedCount()); System.out.println("Updated: " + result.getModifiedCount()); System.out.println("Deleted: " + result.getDeletedCount()); } catch (MongoBulkWriteException e) { System.err.println("Bulk write error occurred: " + e.getMessage()); // Log individual write errors for (BulkWriteError error: e.getWriteErrors()) { System.err.printf("Error at index %d: %s (Code: %d)%n", error.getIndex(), error.getMessage(), error.getCode()); // Log the problematic document Document errorDoc = new Document(error.getDetails()); if (errorDoc != null) { System.err.println("Problematic document: " + errorDoc); } } } catch (Exception e) { System.err.println("Error during bulkWrite: " + e.getMessage()); }
Gravações que podem ser repetidas
Diferentemente do MongoDB, o Amazon DocumentDB não oferece suporte a gravações que possam ser repetidas. Como resultado, você deve implementar uma lógica de repetição personalizada em suas aplicações, especialmente para lidar com problemas de rede ou indisponibilidade temporária de serviços. Uma estratégia de repetição bem implementada geralmente envolve aumentar o atraso entre as tentativas e limitar o número total de tentativas. Consulte Tratamento de erros com lógica de novas tentativas abaixo para obter um exemplo de código de criação de lógica de repetição com tratamento de erros.
Leitura e recuperação de dados de uma coleção do DocumentDB
A consulta de documentos no Amazon DocumentDB gira em torno de vários componentes principais que permitem recuperar e manipular dados com precisão. O find()find(), FiltersFindIterable
A classe Filters é uma classe utilitária no driver de Java do MongoDB que fornece uma API fluente para criar filtros de consulta. Essa classe oferece métodos estáticos de fábrica que criam instâncias de objetos Bson que representam várias condições de consulta. Os métodos mais usados incluem eq() para comparações de igualdade gt(), lt(), gte() e lte() para comparações numéricas, and() e or() para a combinação de várias condições, in() e nin() para testes de associação de matrizes e regex() para correspondência de padrões. A classe foi projetada para ser segura para tipos e fornece melhor verificação de tempo de compilação em comparação com consultas brutas baseadas em documentos, tornando-a a abordagem preferida para criar consultas DocumentDB em aplicações de Java. O tratamento de erros é robusto, com claras exceções lançadas para construções de filtro inválidas.
FindIterable é uma interface especializada projetada para lidar com o resultado do método find(). Ela fornece um rico conjunto de métodos para refinar e controlar a execução de consultas, oferecendo uma API fluente para o encadeamento de métodos. A interface inclui métodos essenciais de modificação de consultas, como limit() para a restrição do número de documentos retornados, skip() para paginação, sort() para ordenação de resultados, projection() para a seleção de campos específicos e hint() para a seleção de índices. As operações de lote, ignorar e limitar em FindIterable são ferramentas essenciais de paginação e gerenciamento de dados que ajudam a controlar como os documentos são recuperados e processados do banco de dados.
O agrupamento em lotes (batchSize) controla quantos documentos o DocumentDB retorna ao cliente em uma única viagem de ida e volta à rede. Quando você define um tamanho de lote, o DocumentDB não retorna todos os documentos correspondentes de uma só vez, mas os retorna em grupos do tamanho de lote especificado.
Ignorar permite que você desvie o ponto de partida de seus resultados, basicamente dizendo ao DocumentDB que pule um número específico de documentos antes de começar a retornar as correspondências. Por exemplo, skip(20) ignorará os primeiros 20 documentos correspondentes. Isso é comumente usado em cenários de paginação em que você deseja recuperar páginas subsequentes de resultados.
O limite restringe o número total de documentos que podem ser retornados de uma consulta. Quando você especifica limit(n), o DocumentDB deixará de retornar documentos depois de retornar 'n' documentos, mesmo que haja mais correspondências no banco de dados.
FindIterable oferece suporte a padrões de iterador e cursor ao recuperar documentos do Amazon DocumentDB. A vantagem de usar FindIterable como iterador é que ela permite o carregamento lento de documentos e só busca documentos quando solicitada pela aplicação. Outro benefício de usar o iterador é que você não é responsável por manter a conexão com o cluster e, portanto, nenhum fechamento explícito da conexão é necessário.
FindIterable também fornece suporte ao MongoCursorMongoCursor é uma implementação específica do driver de Java do MongoDB que fornece controle sobre as operações do banco de dados e o gerenciamento de recursos. Ele implementa a AutoCloseable interface, permitindo o gerenciamento explícito de recursos por meio de try-with-resources blocos, o que é crucial para fechar adequadamente as conexões do banco de dados e liberar recursos do servidor. Por padrão, o cursor expira em 10 minutos e o DocumentDB não oferece a opção de alterar esse comportamento de tempo limite. Ao trabalhar com dados em lote, certifique-se de recuperar o próximo lote de dados antes que o cursor atinja o tempo limite. Uma consideração importante ao usar o MongoCursor é que ele requer um fechamento explícito para evitar vazamentos de recursos.
Nesta seção, vários exemplos são apresentados para find(), Filters e FindIterable.
O exemplo de código a seguir mostra como usar find() para recuperar um único documento usando o campo “restaurantId”:
Document filter = new Document("restaurantId", "REST-21G145"); Document result = collection.find(filter).first();
Embora o uso de Filters permita uma melhor verificação de erros no tempo de compilação, o driver de java também permite que você especifique um filtro Bson diretamente no método find(). O código de exemplo a seguir passa o documento Bson para find():
result = collection.find(new Document("$and", Arrays.asList( new Document("rating.totalReviews", new Document("$gt", 1000)), new Document("priceRange", "$$"))))
O código de exemplo a seguir mostra vários exemplos de uso da classe Filters com find():
FindIterable < Document > results; // Exact match results = collection.find(Filters.eq("name", "Thai Curry Palace")); // Not equal results = collection.find(Filters.ne("cuisine", "Thai")); // find an element in an array results = collection.find(Filters.in("features", Arrays.asList("Private Dining"))); // Greater than results = collection.find(Filters.gt("rating.average", 3.5)); // Between (inclusive) results = collection.find(Filters.and( Filters.gte("rating.totalReviews", 100), Filters.lte("rating.totalReviews", 200))); // AND results = collection.find(Filters.and( Filters.eq("cuisine", "Thai"), Filters.gt("rating.average", 4.5))); // OR results = collection.find(Filters.or( Filters.eq("cuisine", "Thai"), Filters.eq("cuisine", "American"))); // All document where the Field exists results = collection.find(Filters.exists("michelin")); // Regex results = collection.find(Filters.regex("name", Pattern.compile("Curry", Pattern.CASE_INSENSITIVE))); // Find all document where the array contain the list of value regardless of its order results = collection.find(Filters.all("features", Arrays.asList("Private Dining", "Parking"))); // Array size results = collection.find(Filters.size("features", 4));
O exemplo a seguir mostra como encadear as operações de sort(), skip(), limit() e batchSize() em um objeto FindIterable. A ordem de como essas operações são fornecidas influenciará a performance da sua consulta. Como prática recomendada, a ordem dessas operações deve ser sort(), projection(), skip(), limit() e batchSize().
FindIterable < Document > results = collection.find(Filters.gt("rating.totalReviews", 1000)) // Sorting .sort(Sorts.orderBy( Sorts.descending("address.city"), Sorts.ascending("cuisine"))) // Field selection .projection(Projections.fields( Projections.include("name", "cuisine", "priceRange"), Projections.excludeId())) // Pagination .skip(20) .limit(10) .batchSize(2);
O exemplo de código a seguir mostra como criar um iterador em FindIterable. Ele usa a construção forEach do Java para percorrer o conjunto de resultados.
collection.find(Filters.eq("cuisine", "American")).forEach(doc -> System.out.println(doc.toJson()));
No último exemplo de código de find(), ele mostra como usar cursor() para a recuperação de documentos. Ele cria o cursor no bloco try, o que garante que o cursor seja fechado quando o código sair do bloco try.
try (MongoCursor < Document > cursor = collection.find(Filters.eq("cuisine", "American")) .batchSize(25) .cursor()) { while (cursor.hasNext()) { Document doc = cursor.next(); System.out.println(doc.toJson()); } } // Cursor automatically closed
Atualização de documentos existentes em uma coleção do DocumentDB
O Amazon DocumentDB fornece mecanismos flexíveis e poderosos para modificar documentos existentes e inserir novos quando eles não existam. O driver de Java do MongoDB oferece vários métodos para atualizações: updateOne() para atualizações de um único documento, updateMany() para atualizações de vários documentos e replaceOne() para a substituição completa de documentos. Além desses três métodos, UpdatesUpdateOptionsUpdateResult
A classe Updates no driver de Java do MongoDB é uma classe utilitária que fornece métodos estáticos de fábrica para criar operadores de atualização. Ela serve como o principal construtor para construir operações de atualização de maneira segura e legível. Métodos básicos como set(), unset(), e inc() permitem a modificação direta dos documentos. O poder dessa classe se torna evidente ao combinar várias operações usando o método Updates.combine(), que permite que várias operações de atualização sejam executadas atomicamente, garantindo a consistência de dados.
UpdateOptions é uma classe de configuração poderosa no driver de Java do MongoDB, que fornece recursos essenciais de personalização para operações de atualização de documentos. Dois aspectos importantes dessa classe são fornecer suporte a filtros upsert e de matriz para operações de atualização. O recurso upsert, ativado por meio de upsert(true), permite a criação de novos documentos quando nenhum documento correspondente for encontrado durante uma operação de atualização. Por meio de arrayFilters(), a operação de atualização pode atualizar com precisão os elementos da matriz que atendem a critérios específicos.
UpdateResult no driver de Java do MongoDB, fornece o mecanismo de feedback detalhando o resultado de uma operação de atualização. Essa classe encapsula três métricas principais: o número de documentos correspondidos pelos critérios de atualização (matchedCount), o número de documentos realmente modificados (modifiedCount) e informações sobre quaisquer documentos que sofreram upsert (upsertedId). Compreender essas métricas é essencial para o tratamento adequado de erros, a verificação das operações de atualização e a manutenção da consistência de dados nas aplicações.
Atualização e substituição de um único documento
No DocumentDB, a atualização de um único documento pode ser realizada usando o método updateOne(). Esse método usa um parâmetro de filtro, geralmente fornecido pela classe Filters, para identificar o documento a ser atualizado, um parâmetro Update que determina quais campos devem ser atualizados e um parâmetro UpdateOptions opcional para definir opções diferentes para a atualização. O uso do método updateOne() atualizará somente o primeiro documento que corresponda aos critérios de seleção. O código de exemplo a seguir atualiza um único campo de um documento:
collection.updateOne(Filters.eq("restaurantId", "REST-Y2E9H5"), Updates.set("name", "Amazing Japanese sushi"));
Para atualizar vários campos em um documento, use updateOne() com Update.combine() conforme mostrado no exemplo a seguir. O exemplo também mostra como adicionar um item a uma matriz no documento.
List<Bson> updates = new ArrayList<>(); // Basic field updates updates.add(Updates.set("name", "Shanghai Best")); // Array operations updates.add(Updates.addEachToSet("features", Arrays.asList("Live Music"))); // Counter updates updates.add(Updates.inc("rating.totalReviews", 10)); // Combine all updates Bson combinedUpdates = Updates.combine(updates); // Execute automic update with one call collection.updateOne(Filters.eq("restaurantId","REST-1J83NH"), combinedUpdates);
O exemplo de código a seguir demonstra como atualizar um documento no banco de dados. Se o documento especificado não existir, a operação o inserirá automaticamente como um novo documento. Esse código também mostra como usar as métricas disponíveis por meio do objeto UpdateResult.
Bson filter = Filters.eq("restaurantId", "REST-0Y9GL0"); Bson update = Updates.set("cuisine", "Indian"); // Upsert operation UpdateOptions options = new UpdateOptions().upsert(true); UpdateResult result = collection.updateOne(filter, update, options); if (result.getUpsertedId() != null) { System.out.println("Inserted document with _id: " + result.getUpsertedId()); } else { System.out.println("Updated " + result.getModifiedCount() + " document(s)"); }
O exemplo de código a seguir demonstra como substituir completamente um documento existente por um novo usando o método replaceOne(), em vez de atualizar campos individuais. O método replaceOne() sobrescreve o documento inteiro, preservando somente o campo _id do original. Se vários documentos corresponderem aos critérios do filtro, somente o primeiro documento encontrado será substituído.
Document newDocument = new Document() .append("restaurantId", "REST-0Y9GL0") .append("name", "Bhiryani Adda") .append("cuisine", "Indian") .append("rating", new Document() .append("average", 4.8) .append("totalReviews", 267)) .append("features", Arrays.asList("Outdoor Seating", "Live Music")); UpdateResult result = collection.replaceOne( Filters.eq("restaurantId", "REST-0Y9GL0"), newDocument); System.out.printf("Modified %d document%n", result.getModifiedCount());
Atualização de vários documentos
Há duas maneiras de atualizar vários documentos em uma coleção simultaneamente. É possível usar o método updateMany() ou usar UpdateManyModelbulkWrite(). O método updateMany() usa um parâmetro de filtro para selecionar documentos para atualização, o parâmetro Update para identificar os campos a serem atualizados e um parâmetro UpdateOptions opcional para especificar as opções de atualização.
O exemplo de código a seguir demonstra o uso do método updateMany():
Bson filter = Filters.and( Filters.in("features", Arrays.asList("Private Dining")), Filters.eq("cuisine", "Thai")); UpdateResult result1 = collection.updateMany(filter, Updates.set("priceRange", "$$$"));
O exemplo de código a seguir demonstra o método bulkWrite() usando a mesma atualização:
BulkWriteOptions options = new BulkWriteOptions().ordered(false); List < WriteModel < Document >> updates = new ArrayList < > (); Bson filter = Filters.and( Filters.in("features", Arrays.asList("Private Dining")), Filters.eq("cuisine", "Indian")); Bson updateField = Updates.set("priceRange", "$$$"); updates.add(new UpdateManyModel < > (filter, updateField)); BulkWriteResult result = collection.bulkWrite(updates, options); System.out.printf("Modified %d document%n", result.getModifiedCount());
Remoção de documentos de uma coleção do DocumentDB
O driver de Java do MongoDB oferece deleteOne() para a remoção de um único documento e deleteMany() para a remoção de vários documentos que correspondam a critérios específicos. Assim como a atualização, a operação de exclusão também pode ser usada com o método bulkWrite(). Tanto deleteOne() quanto deleteMany() retornam um objeto DeleteResultdeleteMany() para a remoção de vários documentos:
Bson filter = Filters.and( Filters.eq("cuisine", "Thai"), Filters.lt("rating.totalReviews", 50)); DeleteResult result = collection.deleteMany(filter); System.out.printf("Deleted %d document%n", result.getDeletedCount());
Tratamento de erros com lógica de novas tentativas
Uma estratégia robusta de tratamento de erros para o Amazon DocumentDB deve implementar a categorização dos erros em passíveis de nova tentativa (como tempos limite de rede, problemas de conexão) e não passíveis de nova tentativa (como falhas de autenticação, consultas inválidas). Para falhas de operação devido a erros que devem ter nova tentativa, ele deve implementar um intervalo de tempo entre cada nova tentativa, bem como o máximo de tentativas de repetição. As operações CRUD devem estar em um bloco try-catch que capture MongoException
int MAX_RETRIES = 3; int INITIAL_DELAY_MS = 1000; int retryCount = 0; while (true) { try { crud_operation(); //perform crud that will throw MongoException or one of its subclass break; } catch (MongoException e) { if (retryCount < MAX_RETRIES) { retryCount++; long delayMs = INITIAL_DELAY_MS * (long) Math.pow(2, retryCount - 1); try { TimeUnit.MILLISECONDS.sleep(delayMs); } catch (InterruptedException t) { Thread.currentThread().interrupt(); throw new RuntimeException("Retry interrupted", t); } continue; } else throw new RuntimeException("Crud operation failed", e); } }