# Implementing Bulk Writes using Spring Boot for MongoDB

## Introduction
The Spring Data  Framework is used extensively in applications as it makes it easier  to access different kinds of persistence stores.  This article will show how to use  Spring Data MongoDB to implement bulk insertions.

BulkOperations is an interface that contains a list of write operations to be applied to the database. They can be any combination of
`InsertOne`,
`updateOne`
`updateMany`
`replaceOne`
`deleteOne`
`deleteMany`

A bulkOperation can be ordered or unordered. Ordered operations will be applied sequentially and if an error is detected, will return with an error code.  Unordered operations will be applied in parallel and are thus potentially faster, but it is the responsibility of the application to check if there were errors during the operations. For more information please refer to the bulk write operations section of the MongoDB documentation.

## Getting started
A POM file will specify the version of Spring Data that the application will use. Care must be taken to use a version of Spring Data that utilizes a compatible version of the MongoDB Java Driver. You can verify this compatibility in the MongoDB Java API documentation.
```

   
       org.springframework.boot
       spring-boot-starter-data-mongodb
       2.7.2
   
```

## Application class
The top level class is a SpringBootApplication that implements a CommandLineRunner , like so: 
```
@SpringBootApplication
public class SpringDataBulkInsertApplication implements CommandLineRunner {

   @Value("${documentCount}")
   private int count;
   private static final Logger LOG = LoggerFactory
           .getLogger(SpringDataBulkInsertApplication.class);

   @Autowired
   private CustomProductsRepository repository;

   public static void main(String] args) {
       SpringApplication.run(SpringDataBulkInsertApplication.class, args);
   }

   @Override
   public void run(String... args) {

       repository.bulkInsertProducts(count);
       LOG.info("End run");
   }
}

```

Now we need to write a few classes to implement our bulk insertion application.

## Configuration class
We will implement a class that holds the configuration to the MongoClient object that the Spring Data framework will utilize.

The `@Configuration` annotation will allow us to retrieve values to configure access to the MongoDB Environment. For a good explanation of Java-based configuration see [JavaConfig in the Spring reference documentation for more details.

```
@Configuration
public class MongoConfig   {
   @Value("${mongodb.uri}")
   private String uri;

   @Value("${mongodb.database}")
   private String databaseName;

   @Value("${truststore.path}")
   private String trustStorePath;
   @Value("${truststore.pwd}")
   private String trustStorePwd;

   @Value("${mongodb.atlas}")
   private boolean atlas;

   @Bean
   public MongoClient mongo() {
       ConnectionString connectionString = new ConnectionString(uri);
           MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
                   .applyConnectionString(connectionString)
                   .applyToSslSettings(builder -> {
                       if (!atlas) {
                           // Use SSLContext if a trustStore has been provided
                           if (!trustStorePath.isEmpty()) {
                               SSLFactory sslFactory = SSLFactory.builder()
                                       .withTrustMaterial(Paths.get(trustStorePath), trustStorePwd.toCharArray())
                                       .build();
                               SSLContext sslContext = sslFactory.getSslContext();
                               builder.context(sslContext);
                               builder.invalidHostNameAllowed(true);
                           }
                       }
                       builder.enabled(true);
                   })
                   .build();
       return MongoClients.create(mongoClientSettings);
   }

   @Bean
   public MongoTemplate mongoTemplate() throws Exception {
       return new MongoTemplate(mongo(), databaseName);
   }
}
```

In this implementation we are using a flag, mongodb.atlas,  to indicate that this application will  connect to Atlas. If the flag is false, an SSL Context may be created using a trustStore, This presents a certificate for the root certificate authority in the form of a truststore file pointed to by truststore.path, protected by a password (`truststore.pwd`) at the moment of creation.  If needed the client can also offer a keystore file, but this is not implemented.

The parameter mongodb.uri should contain a valid MongoDB URI.  The URI contains the hosts to which the client connects, the user credentials, etcetera. 

## The document class
The relationship between MongoDB collection and the documents that it contains is implemented via a class that is decorated by the @Document annotation.  This class defines the fields of the documents and the annotation defines the name of the collection.

```
@Document("products")
public class Products {

   private static final Logger LOG = LoggerFactory
           .getLogger(Products.class);
   @Id
   private String id;
   private String name;
   private int qty;
   private double price;
   private Date available;
   private Date  unavailable;
   private String skuId;
```

Setters and getters need to be defined for each field.  The @Id annotation indicates our default index. If this field is not specified, MongoDB will assign an ObjectId value which will be unique.

## Repository classes
The repository is implemented with two classes, one  an interface and the other the implementation of the interface. The repository classes flesh out  the interactions of the application with the database. A method in the repository is responsible for the bulk insertion:

```
@Component
public class CustomProductsRepositoryImpl implements CustomProductsRepository {

   private static final Logger LOG = LoggerFactory
           .getLogger(CustomProductsRepository.class);

   @Autowired
   MongoTemplate mongoTemplate;

   public int bulkInsertProducts(int count) {

       LOG.info("Dropping collection...");
       mongoTemplate.dropCollection(Products.class);
       LOG.info("Dropped!");

       Instant start = Instant.now();
       mongoTemplate.setWriteConcern(WriteConcern.W1.withJournal(true));

       Products [] productList = Products.RandomProducts(count);
       BulkOperations bulkInsertion = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, Products.class);

       for (int i=0; i