Execução de operações CRUD no Amazon DocumentDB com Java - Amazon DocumentDB

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 Java do MongoDB.

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ê está trabalhando. O método mais básico para inserir um documento individual na coleção éinsertOne(). Para inserir vários documentos ao mesmo tempo, você pode usar o insertMany() método, 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 é. 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 Amazon DocumentDBB. A inserção de um único documento é realizada usando o insertOne() método. Esse método usa a BsonDocumentpara inserção e retorna um InsertOneResultobjeto que pode ser usado para obter a ID do objeto do novo documento inserido. O código de exemplo abaixo mostra a inserção de um documento de restaurante na coleção:

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 usarinsertOne(), 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á o seguinteMongoWriteException:

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={}}.

Inserir muitos ()

Os principais métodos usados para inserir muitos documentos em uma coleção são insertMany () e. bulkWrite()

O insertMany() método é 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());

bulkWrite()

O bulkWrite() método permite realizar várias operações de gravação (inserir, atualizar, excluir) em um único lote. Você pode usar bulkWrite() quando precisar realizar diferentes tipos de operações em um único lote, como inserir alguns documentos e atualizar outros. bulkWrite()suportam 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 são bem-sucedidas e outras falham.

Ao trabalhar com o bulkWrite() método, existem algumas classes essenciais que são necessárias. Primeiro, a WriteModelclasse serve como classe base para todas as operações de gravação e com implementações específicas InsertOneModel, como,, UpdateOneModelUpdateManyModelDeleteOneModel, e para DeleteManyModellidar com diferentes tipos de operações.

A BulkWriteOptionsclasse é necessária para configurar o comportamento de operações em massa, como definir a ordered/unordered execução ou ignorar a validação de documentos. A BulkWriteResultclasse fornece informações detalhadas sobre os resultados da execução, incluindo contagens de documentos inseridos, atualizados e excluídos.

Para o tratamento de erros, a MongoBulkWriteExceptionclasse é crucial, pois contém informações sobre quaisquer falhas durante a operação em massa, enquanto a BulkWriteErrorclasse fornece detalhes específicos sobre falhas de operação individuais. O código a seguir mostra um exemplo de inserção de uma lista de documentos, bem como atualização e exclusão de um único documento, tudo dentro da execução de uma única chamada de bulkWrite() método. O código também mostra como trabalhar com BulkWriteOptionse BulkWriteResult, bem como o tratamento adequado de erros da bulkWrite() operação.

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 repetitivas. Como resultado, você deve implementar uma lógica de repetição personalizada em seus aplicativos, 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. Veja Tratamento de erros com lógica de repetição abaixo um exemplo de código de criação de lógica de repetição com tratamento de erros.

Lendo e recuperando 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()método é a consulta fundamental APIs nos drivers Java do MongoDB. Ele permite a recuperação complexa de dados com várias opções para filtrar, classificar e projetar resultados. Além do find() método, FiltersFindIterableexistem dois outros componentes fundamentais que fornecem os blocos de construção para operações de consulta nos drivers Java do MongoDB.

A Filters classe é uma classe utilitária no driver 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 Bson objetos 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, para combinar várias condições, and() or() para testes de associação de matrizes in() e nin() para correspondência de padrões. regex() 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 aplicativos 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 find() método. Ele 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() restringir o número de documentos retornados, skip() paginação, sort() ordenar resultados, projection() selecionar campos específicos e hint() selecionar índices. As operações em lote, ignorar e limitar 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 batching (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.

Skip 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ê especificarlimit(n), o DocumentDB deixará de retornar documentos depois de retornar 'n' documentos, mesmo que haja mais correspondências no banco de dados.

FindIterablesuporta padrões de iterador e cursor ao recuperar documentos do Amazon DocumentDB. A vantagem de usar FindIterable como iterador é que ele permite o carregamento lento de documentos e só busca documentos quando solicitado pelo aplicativo. 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.

FindIterabletambém fornecem suporte MongoCursorque permite que padrões de cursor sejam usados ao trabalhar com consultas do Amazon DocumentDB. MongoCursoré uma implementação específica do driver Java 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 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 FindIterable e.

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 Filters permita uma melhor verificação de erros no tempo de compilação, o driver java também permite que você especifique um Bson filtro diretamente no find() método. O código de exemplo a seguir passa Bson o documento parafind():

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 Filters classe comfind():

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 FindIterable objeto. A ordem de como essas operações são fornecidas influenciará o desempenho da sua consulta. Como prática recomendada, a ordem dessas operações deve ser sort() projection()skip(),, limit() batchSize() e.

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 código de exemplo a seguir mostra a criação de um iterador em. FindIterable Ele usa a forEach construção do Java para percorrer o conjunto de resultados.

collection.find(Filters.eq("cuisine", "American")).forEach(doc -> System.out.println(doc.toJson()));

No último exemplo find() de código, ele mostra como usar cursor() para 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

Atualizando 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 existem. O driver Java do MongoDB oferece vários métodos para atualizaçõesupdateOne(): para atualizações de um único documentoupdateMany(), para atualizações de vários documentos replaceOne() e para substituição completa de documentos. Além desses três métodos,, UpdatesUpdateOptions, e UpdateResultexistem outros componentes fundamentais que fornecem os blocos de construção para operações de atualização nos drivers Java do MongoDB.

A Updates classe no driver Java do MongoDB é uma classe utilitária que fornece métodos estáticos de fábrica para criar operadores de atualização. Ele 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 Updates.combine() método que permite que várias operações de atualização sejam executadas atomicamente, garantindo a consistência dos dados.

UpdateOptionsé uma classe de configuração poderosa no driver 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 deupsert(true), permite a criação de novos documentos quando nenhum documento correspondente for encontrado durante uma operação de atualização. Por meio arrayFilters() disso, a operação de atualização pode atualizar com precisão os elementos da matriz que atendem a critérios específicos.

UpdateResultno driver 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 alterados (). 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 dos dados nos aplicativos.

Atualizar e substituir 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 Filters classe, para identificar o documento a ser atualizado, um parâmetro Updat e que determina quais campos devem ser atualizados e um UpdateOptions parâmetro opcional para definir opções diferentes para a atualização. O uso do updateOne() método 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. Este 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 UpdateResult objeto.

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 replaceOne() método, em vez de atualizar campos individuais. O replaceOne() método sobrescreve o documento inteiro, preservando somente o _id campo 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());

Atualizar vários documentos

Há duas maneiras de atualizar vários documentos em uma coleção simultaneamente. Você pode usar o updateMany() método ou usar o UpdateManyModelcom o bulkWrite() método. O updateMany() método usa um parâmetro de filtro para selecionar documentos para atualização, o Update parâmetro para identificar os campos a serem atualizados e um UpdateOptions parâmetro opcional para especificar as opções de atualização.

O código de exemplo a seguir demonstra o uso do updateMany() método:

Bson filter = Filters.and( Filters.in("features", Arrays.asList("Private Dining")), Filters.eq("cuisine", "Thai")); UpdateResult result1 = collection.updateMany(filter, Updates.set("priceRange", "$$$"));

O código de exemplo a seguir demonstra o bulkWrite() método 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());

Removendo documentos de uma coleção do DocumentDB

O driver Java do MongoDB deleteOne() oferece a remoção de um único documento deleteMany() e 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 bulkWrite() método. Ambos deleteOne() e deleteMany() retornam um DeleteResultobjeto que fornece informações sobre o resultado da operação, incluindo a contagem de documentos excluídos. Veja a seguir um exemplo de uso deleteMany() para remover 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 repetição

Uma estratégia robusta de tratamento de erros para o Amazon DocumentDB deve implementar a categorização dos erros em repetitivos (como tempos limite de rede, problemas de conexão) e não repetidos (como falhas de autenticação, consultas inválidas). Para falhas de operação devido a erros que devem ser repetidos, 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 captura MongoExceptione suas subclasses. Além disso, deve incluir monitoramento e registro de erros para visibilidade operacional. Veja a seguir um exemplo de código que mostra como implementar o tratamento de erros de repetição:

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