Implementação de gravações em massa usando o Spring Boot para MongoDB
Avalie esse Tutorial
A Spring Data Framework é usada extensivamente em aplicativos, pois facilita o acesso a diferentes tipos de armazenamentos de persistência. Este artigo mostrará como usar o Spring Data MongoDB para implementar inserções em massa.
BulkOperations é uma interface que contém uma lista de operações de gravação a serem aplicadas no banco de dados. Podem ser qualquer combinação de
InsertOne
, updateOne
updateMany
replaceOne
deleteOne
deleteMany
Uma operação em massa pode ser ordenada ou não ordenada. As operações ordenadas serão aplicadas sequencialmente e, se for detectado um erro, retornarão com um código de erro. As operações não ordenadas serão aplicadas em paralelo e, portanto, são potencialmente mais rápidas, mas é responsabilidade do aplicativo verificar se houve erros durante as operações. Para obter mais informações, consulte a seção de operações de gravação em massa da documentação do MongoDB.
Um arquivo POM especificará a versão do Spring Data que o aplicativo usará. Deve-se tomar cuidado ao usar uma versão do Spring Data que utilize uma versão compatível do MongoDB Java Driver. Você pode verificar essa compatibilidade na documentação da API Java do MongoDB.
1 <dependencies> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-data-mongodb</artifactId> 5 <version>2.7.2</version> 6 </dependency>
A classe de nível superior é um SpringBootApplication que implementa um CommandLineRunner, da seguinte forma:
1 @SpringBootApplication 2 public class SpringDataBulkInsertApplication implements CommandLineRunner { 3 4 @Value("${documentCount}") 5 private int count; 6 private static final Logger LOG = LoggerFactory 7 .getLogger(SpringDataBulkInsertApplication.class); 8 9 @Autowired 10 private CustomProductsRepository repository; 11 12 public static void main(String[] args) { 13 SpringApplication.run(SpringDataBulkInsertApplication.class, args); 14 } 15 16 17 @Override 18 public void run(String... args) { 19 20 repository.bulkInsertProducts(count); 21 LOG.info("End run"); 22 } 23 }
Agora precisamos gravar algumas classes para implementar nosso aplicativo de inserção em massa.
Implementaremos uma classe que contém a configuração do objeto MongoClient que o framework do Spring Data utilizará.
A anotação
@Configuration
nos permitirá recuperar valores para configurar o acesso ao ambiente MongoDB. Para obter uma boa explicação da configuração baseada em Java, consulte JavaConfig na documentação de referência do Spring para obter mais detalhes.1 @Configuration 2 public class MongoConfig { 3 @Value("${mongodb.uri}") 4 private String uri; 5 6 @Value("${mongodb.database}") 7 private String databaseName; 8 9 @Value("${truststore.path}") 10 private String trustStorePath; 11 @Value("${truststore.pwd}") 12 private String trustStorePwd; 13 14 @Value("${mongodb.atlas}") 15 private boolean atlas; 16 17 @Bean 18 public MongoClient mongo() { 19 ConnectionString connectionString = new ConnectionString(uri); 20 MongoClientSettings mongoClientSettings = MongoClientSettings.builder() 21 .applyConnectionString(connectionString) 22 .applyToSslSettings(builder -> { 23 if (!atlas) { 24 // Use SSLContext if a trustStore has been provided 25 if (!trustStorePath.isEmpty()) { 26 SSLFactory sslFactory = SSLFactory.builder() 27 .withTrustMaterial(Paths.get(trustStorePath), trustStorePwd.toCharArray()) 28 .build(); 29 SSLContext sslContext = sslFactory.getSslContext(); 30 builder.context(sslContext); 31 builder.invalidHostNameAllowed(true); 32 } 33 } 34 builder.enabled(true); 35 }) 36 .build(); 37 return MongoClients.create(mongoClientSettings); 38 } 39 40 @Bean 41 public MongoTemplate mongoTemplate() throws Exception { 42 return new MongoTemplate(mongo(), databaseName); 43 } 44 }
Nesta implementação, estamos usando um sinalizador, mongodb.atlas, para indicar que este aplicativo se conectará ao Atlas. Se o sinalizador for falso, um contexto SSL poderá ser criado usando um trustStore, que apresenta um certificado para a autoridade de certificação raiz na forma de um arquivo truststore apontado por truststore.path e protegido por uma senha (
truststore.pwd
) no momento da criação. Se necessário, o cliente também pode oferecer um arquivo keystore, mas isso não é implementado.O parâmetro mongodb.uri deve conter um URI válido do MongoDB. O URI contém os hosts aos quais o cliente se conecta, as credenciais do usuário, etc.
A relação entre a coleção do MongoDB e os documentos que ela contém é implementada por meio de uma classe feita pela anotação @Document. Esta classe define os campos dos documentos e a anotação define o nome da coleção.
1 @Document("products") 2 public class Products { 3 4 private static final Logger LOG = LoggerFactory 5 .getLogger(Products.class); 6 @Id 7 private String id; 8 private String name; 9 private int qty; 10 private double price; 11 private Date available; 12 private Date unavailable; 13 private String skuId;
É necessário definir setters e getters para cada campo. A anotação @Id indica nosso índice padrão. Se este campo não for especificado, o MongoDB atribuirá um valor ObjectId que será único.
O repositório é implementado com duas classes, uma interface e a outra a implementação da interface. As classes de repositório aprimoram as interações do aplicativo com o banco de dados. Um método no repositório é responsável pela inserção em massa:
1 @Component 2 public class CustomProductsRepositoryImpl implements CustomProductsRepository { 3 4 private static final Logger LOG = LoggerFactory 5 .getLogger(CustomProductsRepository.class); 6 7 @Autowired 8 MongoTemplate mongoTemplate; 9 10 public int bulkInsertProducts(int count) { 11 12 LOG.info("Dropping collection..."); 13 mongoTemplate.dropCollection(Products.class); 14 LOG.info("Dropped!"); 15 16 Instant start = Instant.now(); 17 mongoTemplate.setWriteConcern(WriteConcern.W1.withJournal(true)); 18 19 Products [] productList = Products.RandomProducts(count); 20 BulkOperations bulkInsertion = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, Products.class); 21 22 for (int i=0; i<productList.length; ++i) 23 bulkInsertion.insert(productList[i]); 24 25 BulkWriteResult bulkWriteResult = bulkInsertion.execute(); 26 27 LOG.info("Bulk insert of "+bulkWriteResult.getInsertedCount()+" documents completed in "+ Duration.between(start, Instant.now()).toMillis() + " milliseconds"); 28 return bulkWriteResult.getInsertedCount(); 29 } 30 }
Neste exemplo, estamos criando uma lista aleatória de produtos, que depois são inseridos em massa e desordenadamente. Especificamos uma preocupação de gravação de 1, o que significa que o servidor reconhecerá a operação assim que o Primário escrever a operação no registro do diário.
Essa preocupação de gravação resulta em inserções mais rápidas, mas há uma chance de perda de dados se o Primário falhar ou se ocorrer uma eleição e os dados ainda não tiverem sido replicados. Para evitar esse risco, use a preocupação de gravação
Majority
.