Tutorial: Spring Data Framework Integration
Overview
In this tutorial, you can learn how to use Spring Data MongoDB with the Java driver to perform high-performance bulk inserts in a Spring Boot application.
Spring Data MongoDB
Spring Data MongoDB is the official Spring Data ODM for MongoDB. It allows you to
interact with MongoDB by using plain old Java objects (POJOs) and repository
abstraction. It supports MongoDB-specific features like dynamic queries,
indexing, and nested document mapping while reducing boilerplate code such as
manual find()
and update()
calls.
Dependency Injection
Dependency injection (DI) is a core principle of the Spring Framework. It allows objects, called beans, to be created and managed by the Spring container, then injected into other beans that use them. This is distinct from typical object-oriented development where classes are responsible for initializing and constructing the objects they use.
For more information about dependency injection, see the Dependency Injection page of the Spring Framework documentation.
The Spring Data BulkOperations Interface
BulkOperations is a Spring Data MongoDB interface that contains a list of write operations that can be applied to your database. It can handle any combination of the following operations which map to similar MongoDB Java Driver operations:
insert
updateOne
updateMany
replaceOne
deleteOne
deleteMany
upsert
A BulkOperation can be ordered or unordered. Ordered bulk operations run operations sequentially, and if an error is detected, return with an error code. Unordered operations are run in parallel, which means they are typically faster. However, you must manually check if there were errors during the operations.
For more information about bulk operations, see the following resources:
BulkOperations in the Spring Framework API documentation
Bulk Write Operations in this guide
Bulk Write Operations in the MongoDB server manual
Tutorial
You can find the completed sample app for this tutorial in the SpringDataBulkInsert sample project GitHub repository.
Note
Imports Not Specified
The import statements required for the files in the tutorial have not been included on the page. See the GitHub repository for complete files.
Prerequisites
Ensure you have the following components installed and set up before you start this tutorial:
Add Dependencies
Ensure that you use a Spring Data MongoDB version that is compatible with the MongoDB Java Driver and Java versions you are using. For compatibility specifications, see the Requirements page of the Spring Data MongoDB documentation, and the Compatibility page of this guide.
Note
If you used the Spring Initializr or a clone of the Spring Boot sample project to create your project, versioning compatibility has already been accounted for, and the spring-boot-starter-data-mongodb
component will already be included in your pom.xml
file.
Add the following dependencies to your pom.xml
:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> <version>3.2.5 </version> </dependency> <dependency> <groupId>net.datafaker</groupId> <artifactId>datafaker</artifactId> <version>2.4.3</version> </dependency>
The datafaker
dependency is used to generate a large quantity of Product
objects to use in the bulk write operation.
Configure your MongoClient
The MongoConfig
class contains the configuration for the MongoClient
that will allow the Spring Data framework to interact with the MongoDB server,
and sets other configuration options. For more information about configuration
options, see the Specify Connection Options
page of this guide.
This application uses @Configuration
annotations for classes, @Bean
annotations for methods, and @Value
annotations for parameter conversion.
These annotations allow the Spring IoC container to manage objects. For a
detailed explanation of these annotations, see the following sections of the
Spring Data framework guide:
@Configuration
and@Bean
annotations: Java-based Container Configuration@Value
annotations: Using @Value
Create a MongoConfig.java
file and add the following configuration and
template classes to set up your MongoDB connection:
public class MongoConfig { private String uri; private String databaseName; public MongoClient mongo() { ConnectionString connectionString = new ConnectionString(uri); MongoClientSettings mongoClientSettings = MongoClientSettings.builder() .applyConnectionString(connectionString) .build(); return MongoClients.create(mongoClientSettings); } public MongoTemplate mongoTemplate() throws Exception { return new MongoTemplate(mongo(), databaseName); } }
Note
API vs Interface
This implementation uses the MongoTemplate
API, rather than extending a
Spring Data repository interface such as MongoRepository
, to allow for
fine-grained control over bulk operations.
Set the values of your connection string (mongodb.uri
),
database name (mongodb.database
), and bulk operation count (documentCount
) in your application.properties
file:
mongodb.database=bulk mongodb.uri=<connection string> documentCount=25000
This tutorial uses a database named bulk
, and creates 25000 documents to save. Replace the <connection
string>
placeholder with a connection string for your Atlas deployment. For
more information, see the Create a Connection String section of
this guide.
Map Your Object to a Document
Mapping a class to a collection allows the Spring IoC container to store objects
as MongoDB documents. You can use the @Document
annotation to specify which
collection a class maps to. For more information about mapping objects to MongoDB
documents, see the Mapping Annotation Overview
section of the Spring Data MongoDB documentation.
The @Id
annotation in the following code indicates that the id
field
maps to the _id
field which is used as a unique identifier in MongoDB documents. You can choose any field of
any type, except arrays, to be the unique identifier. For more information, see
the How the _id field is handled in the mapping layer
section of the Spring Data MongoDB documentation.
Create a Product.java
file to define your Product
class and map it to
your products
collection with the following code:
public class Product { private static final Logger LOG = LoggerFactory .getLogger(Products.class); private String id; private String name; private int qty; private double price; private Date available; private Date unavailable; private String skuId; public Product(String name, int qty, double price, Date available, Date unavailable, String skuId) { this.name = name; this.qty = qty; this.price = price; this.available = available; this.unavailable = unavailable; this.skuId = skuId; } public static Product [] RandomProducts( int count) { Faker faker = new Faker(); Random rand = new Random(); Product [] retProds = new Product[count]; for (int i=0; i<count; ++i) { Product p = new Product( faker.animal().name(), 1+rand.nextInt(998), 10.0+rand.nextInt(9999), new Date(), new Date(), faker.idNumber().valid()); retProds[i] = p; } return retProds; } // Getters and setters }
The Product
class includes a static method that generates an array of
Product
objects. You may also define getters and setters for each field.
Define a Repository to Store Your Products
The ProductRepository
will manage a collection of Product
objects.
Your ProductRepository
references the client creation methods defined in the MongoConfig
class, which you
annotated with @Bean
. By using the @Autowired
annotation with the
mongoTemplate
variable together with a constructor that includes
mongoTemplate
as an argument, the Spring container uses constructor
injection to inject a mongoTemplate
dependency. For more information about
constructor injection, see the Constructor-based Dependency Injection
section of the Spring Framework documentation.
Create a ProductRepository.java
file and define your ProductRepository
class to manage a collection of Product
objects with the following code:
public class ProductRepository { private static final Logger LOG = LoggerFactory .getLogger(ProductRepository.class); private final MongoTemplate mongoTemplate; public ProductRepository(MongoTemplate mongoTemplate){ this.mongoTemplate = mongoTemplate; } public void updateProductQuantity(String name, int newQuantity) { Query query = new Query(Criteria.where("name").is(name)); Update update = new Update(); update.set("quantity", newQuantity); UpdateResult result = mongoTemplate.updateFirst(query, update, Product.class); if(result == null) LOG.error("No documents updated"); else LOG.info(result.getModifiedCount() + " document(s) updated.."); } public int bulkInsertProducts(int count) { LOG.info("Dropping collection..."); mongoTemplate.dropCollection(Product.class); LOG.info("Dropped!"); Instant start = Instant.now(); mongoTemplate.setWriteConcern(WriteConcern.W1.withJournal(true)); Product [] productList = Product.RandomProducts(count); BulkOperations bulkInsertion = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, Product.class); for (int i=0; i<productList.length; ++i) bulkInsertion.insert(productList[i]); BulkWriteResult bulkWriteResult = bulkInsertion.execute(); LOG.info("Bulk insert of "+bulkWriteResult.getInsertedCount()+" documents completed in "+ Duration.between(start, Instant.now()).toMillis() + " milliseconds"); return bulkWriteResult.getInsertedCount(); } }
The bulkInsertProducts()
method uses unordered bulk inserts, which can
improve performance by not guaranteeing the order of operations.
Perform a Bulk Operation
The main application class triggers the ProductRepository
to generate the
specified number of Product
objects and save them to your MongoDB database. It uses
the @Autowired
annotation to inject a ProductRepository
, and implements logging.
Add the following code to your main class to run your application:
public class MyProjectName implements CommandLineRunner { private int count; private static final Logger LOG = LoggerFactory .getLogger(MyProjectName.class); private ProductRepository repository; public static void main(String[] args) { SpringApplication.run(MyProjectName.class, args); } public void run(String... args) throws Exception { repository.bulkInsertProducts(count); LOG.info("End run"); System.exit(1); } }
Conclusion
Spring Data MongoDB provides a high-level abstraction for working with MongoDB. It can simplify application architecture by supporting automatic dependency injection, which eliminates the need for manual client configuration and complex query handling. By reducing boilerplate code and supporting object-oriented data access, it can streamline data access and promote a clear separation of concerns.
More Resources
For more information about the Spring Data Framework, see the following resources:
For support or to contribute to the MongoDB Community, see the MongoDB Developer Community.