Spring Boot là cách đơn giản nhất để khởi tạo nhanh một dự án Spring, còn MongoDB là cơ sở dữ liệu NoSQL phổ biến nhất hiện nay. Trong bài viết này, chúng ta cùng tìm hiểu cách tích hợp Spring Boot với MongoDB.

Spring Boot với MongoDB
Chúng ta cần các API sau để làm việc với Spring Boot và cơ sở dữ liệu MongoDB:
- Spring Data MongoDB
- Spring Boot
Có hai cách tiếp cận để kết nối với cơ sở dữ liệu MongoDB: MongoRepository và MongoTemplate.
Chúng ta sẽ tìm hiểu xem mỗi API cung cấp những gì, ưu điểm của từng cách, và khi nào nên sử dụng chúng tùy theo từng trường hợp cụ thể.
Chúng ta sẽ sử dụng công cụ Spring Initializr để thiết lập dự án một cách nhanh chóng.
Giờ thì bắt đầu thôi!
Thiết lập dự án Spring Boot với MongoDB
Chúng ta sẽ sử dụng công cụ **Spring Initializr** để thiết lập dự án một cách nhanh chóng. Trong dự án này, chúng ta chỉ sử dụng hai dependency như được hiển thị bên dưới:

Tải dự án xuống và giải nén. Sau đó, nhập dự án vào IDE yêu thích của bạn – **Eclipse** hoặc **IntelliJ IDEA**.
Maven Dependencies
Mặc dù chúng ta đã thiết lập dự án bằng công cụ Spring Initializr, nếu bạn muốn cấu hình thủ công, dự án này sử dụng hệ thống xây dựng Maven và dưới đây là các dependency được sử dụng:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="<https://maven.apache.org/POM/4.0.0>" xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>"
xsi:schemaLocation="<https://maven.apache.org/POM/4.0.0> <https://maven.apache.org/xsd/maven-4.0.0.xsd>">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>spring-boot-mongodb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-mongodb</name>
<description>Spring Boot MongoDB Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Hãy đảm bảo sử dụng phiên bản ổn định của Spring Boot từ Maven Central.
Lớp Model Spring Boot MongoDB
Chúng ta có một lớp model đơn giản là User.java.
package com.journaldev.bootifulmongodb.model;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class User {
@Id
private String userId;
private String name;
private Date creationDate = new Date();
private Map<String, String> userSettings = new HashMap<>();
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Map<String, String> getUserSettings() {
return userSettings;
}
public void setUserSettings(Map<String, String> userSettings) {
this.userSettings = userSettings;
}
}
Các API Spring Boot MongoDB
Ứng dụng của chúng ta sẽ có các chức năng và tương tác với cơ sở dữ liệu như sau:
- Lấy danh sách tất cả người dùng
- Lấy thông tin người dùng theo ID
- Lấy cài đặt của người dùng
- Lấy một khóa cụ thể từ đối tượng Map
- Thêm hoặc cập nhật cài đặt người dùng
Spring Data MongoDB – MongoRepository
Bây giờ chúng ta sẽ sử dụng Spring Data MongoDB Repository để truy cập dữ liệu. MongoRepository trong Spring Data cung cấp sẵn các chức năng phổ biến mà chúng ta có thể dễ dàng tích hợp và sử dụng. Giờ hãy định nghĩa interface Repository của chúng ta.
package com.journaldev.bootifulmongodb.dal;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.journaldev.bootifulmongodb.model.User;
@Repository
public interface UserRepository extends MongoRepository<User, String> {
}
Định nghĩa các thuộc tính kết nối MongoDB
Trước khi xây dựng controller, điều quan trọng là chúng ta cần thiết lập kết nối với một phiên bản MongoDB cục bộ. Chúng ta sẽ sử dụng các thuộc tính cấu hình của Spring Boot để thực hiện điều này.
#Local MongoDB config
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.username=root
spring.data.mongodb.password=root
spring.data.mongodb.database=user_db
spring.data.mongodb.port=27017
spring.data.mongodb.host=localhost
# App config
server.port=8102
spring.application.name=BootMongo
server.context-path=/user
Vậy là ứng dụng sẽ chạy trên cổng 8102 và kết nối đến một phiên bản MongoDB local với thông tin đăng nhập được cung cấp. Nếu bạn đang sử dụng instance MongoDB local không bật xác thực, bạn chỉ cần xóa ba dòng cấu hình đầu tiên.
Định nghĩa controller trong Spring
Cuối cùng, hãy cùng tạo lớp Controller cho ứng dụng của chúng ta.
package com.journaldev.bootifulmongodb.controller;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.journaldev.bootifulmongodb.dal.UserRepository;
import com.journaldev.bootifulmongodb.model.User;
@RestController
@RequestMapping(value = "/")
public class UserController {
private final Logger LOG = LoggerFactory.getLogger(getClass());
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Chúng ta chỉ cần Autowired (tự động gán) phụ thuộc của interface repository và sẽ sử dụng nó ngay sau đây.
Định nghĩa các API
Đối với các chức năng đã đề cập trước đó, giờ đây chúng ta sẽ tạo các API và sử dụng dependency userRepository, đối tượng này bên trong sẽ sử dụng API của Spring Data MongoRepository.
Lưu ý rằng chúng ta không cần viết bất kỳ đoạn code tương tác cơ sở dữ liệu nào trong interface vì Spring Data đã xử lý tất cả cho chúng ta.
Lấy danh sách tất cả người dùng
@RequestMapping(value = "", method = RequestMethod.GET)
public List<User> getAllUsers() {
LOG.info("Getting all users.");
return userRepository.findAll();
}
findAll() chỉ là một phương thức được Spring Data MongoRepository cung cấp sẵn bên trong.
Lấy người dùng theo ID
Bây giờ, chúng ta sẽ lấy thông tin của một người dùng cụ thể thông qua ID.
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public User getUser(@PathVariable String userId) {
LOG.info("Getting user with ID: {}.", userId);
return userRepository.findOne(userId);
}
findOne() chỉ là một phương thức được Spring Data MongoRepository cung cấp sẵn để lấy một đối tượng theo ID.
Thêm người dùng mới
Chúng ta sẽ thêm một người dùng mới trong hàm dưới đây.
@RequestMapping(value = "/create", method = RequestMethod.POST)
public User addNewUsers(@RequestBody User user) {
LOG.info("Saving user.");
return userRepository.save(user);
}
Lấy cài đặt của người dùng
Bây giờ khi chúng ta đã thêm dữ liệu mẫu vào cơ sở dữ liệu, hãy thử trích xuất một phần trong số đó.
@RequestMapping(value = "/settings/{userId}", method = RequestMethod.GET)
public Object getAllUserSettings(@PathVariable String userId) {
User user = userRepository.findOne(userId);
if (user != null) {
return user.getUserSettings();
} else {
return "User not found.";
}
}
Lấy một cài đặt cụ thể của người dùng
@RequestMapping(value = "/settings/{userId}/{key}", method = RequestMethod.GET)
public String getUserSetting(@PathVariable String userId, @PathVariable String key) {
User user = userRepository.findOne(userId);
if (user != null) {
return user.getUserSettings().get(key);
} else {
return "User not found.";
}
}
Lưu ý trong truy vấn trên, chúng ta lấy đối tượng người dùng, sau đó trích xuất toàn bộ Map chứa Setting (có thể chứa hàng ngàn đối tượng), và cuối cùng mới lấy được giá trị cần thiết. Đây là một điểm hạn chế của truy vấn trong Spring Data khi chúng ta sử dụng nó như một API trực tiếp.
Thêm cài đặt mới cho người dùng
Hãy thử thêm một vài dữ liệu vào một người dùng đã tồn tại:
@RequestMapping(value = "/settings/{userId}/{key}/{value}", method = RequestMethod.GET)
public String addUserSetting(@PathVariable String userId, @PathVariable String key, @PathVariable String value) {
User user = userRepository.findOne(userId);
if (user != null) {
user.getUserSettings().put(key, value);
userRepository.save(user);
return "Key added";
} else {
return "User not found.";
}
}
Với toàn bộ đoạn code mà chúng ta đã viết, có thể thấy rõ rằng chúng ta không cần viết bất kỳ dòng code nào để truy cập cơ sở dữ liệu, ngoài việc định nghĩa interface repository và tự động gán phụ thuộc (autowire dependency). Đây chính là sự tiện lợi mà Spring Data MongoRepository API mang lại cho chúng ta. Tuy nhiên, nó cũng có một số hạn chế. Chúng ta sẽ phân tích kỹ hơn về điều này sau khi triển khai phiên bản sử dụng MongoTemplate. Bây giờ, hãy cùng bắt đầu với phần đó.
Spring Data MongoDB – MongoTemplate
Tại phần này, chúng ta sẽ định nghĩa các truy vấn cơ sở dữ liệu sử dụng MongoTemplate. Với MongoTemplate, bạn sẽ thấy rằng chúng ta có khả năng kiểm soát chi tiết hơn đối với các truy vấn và dữ liệu trả về trong kết quả.
Định nghĩa Interface cho lớp truy cập dữ liệu (DAL)
Để cung cấp một lớp hợp đồng cho tầng truy cập dữ liệu, chúng ta sẽ bắt đầu bằng cách định nghĩa một interface hoạt động tương tự như các phương thức dựng sẵn của Spring Data.
package com.journaldev.bootifulmongodb.dal;
import java.util.List;
import com.journaldev.bootifulmongodb.model.User;
public interface UserDAL {
List<User> getAllUsers();
User getUserById(String userId);
User addNewUser(User user);
Object getAllUserSettings(String userId);
String getUserSetting(String userId, String key);
String addUserSetting(String userId, String key, String value);
}
Triển khai Interface tầng truy cập dữ liệu (DAL)
Hãy tiếp tục và định nghĩa các phương thức này.
package com.journaldev.bootifulmongodb.dal;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;
import com.journaldev.bootifulmongodb.model.User;
@Repository
public class UserDALImpl implements UserDAL {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public List<User> getAllUsers() {
return mongoTemplate.findAll(User.class);
}
@Override
public User getUserById(String userId) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
return mongoTemplate.findOne(query, User.class);
}
@Override
public User addNewUser(User user) {
mongoTemplate.save(user);
// Now, user object will contain the ID as well
return user;
}
@Override
public Object getAllUserSettings(String userId) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
User user = mongoTemplate.findOne(query, User.class);
return user != null ? user.getUserSettings() : "User not found.";
}
@Override
public String getUserSetting(String userId, String key) {
Query query = new Query();
query.fields().include("userSettings");
query.addCriteria(Criteria.where("userId").is(userId).andOperator(Criteria.where("userSettings." + key).exists(true)));
User user = mongoTemplate.findOne(query, User.class);
return user != null ? user.getUserSettings().get(key) : "Not found.";
}
@Override
public String addUserSetting(String userId, String key, String value) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
User user = mongoTemplate.findOne(query, User.class);
if (user != null) {
user.getUserSettings().put(key, value);
mongoTemplate.save(user);
return "Key added.";
} else {
return "User not found.";
}
}
}
Các phương thức được triển khai trong lớp trên sử dụng dependency MongoTemplate. Hãy xem cách phương thức getUserById(...) lấy thông tin người dùng – chúng ta tạo một truy vấn và truyền vào các tham số cần thiết. Điều đáng chú ý hơn chính là truy vấn trong phương thức getUserSetting. Hãy cùng phân tích điều đã xảy ra ở phần trên:
- Chúng ta đã xây dựng các truy vấn với điều kiện
criteriađể kiểm tra sự bằng nhau. - Phương thức
includedùng để chỉ định các trường sẽ được truy xuất từ cơ sở dữ liệu. Trong trường hợp này, chỉ có khóauserSettingsđược lấy ra, điều này giúp tiết kiệm tài nguyên bằng cách không truy xuất những dữ liệu không cần thiết. - Ngoài ra, chúng ta truy vấn dựa trên cả
uservàmap key. Nếu một trong hai không tìm thấy, dữ liệu rỗng sẽ được trả về, nghĩa là khóa cần tìm không tồn tại. Điều này giúp tránh việc truy xuất toàn bộ đối tượngUserkhi khóa đó không có trong cơ sở dữ liệu.
Chạy Thử Ứng Dụng Spring Data MongoDB
Chúng ta có thể chạy ứng dụng này chỉ với một lệnh đơn giản:
mvn spring-boot:run
Sau khi ứng dụng đã khởi động, chúng ta có thể thử lưu một người dùng mới bằng cách sử dụng API sau:
<https://localhost:8102/user/create>
Vì đây là một yêu cầu POST, chúng ta sẽ gửi kèm dữ liệu dưới dạng JSON:
{
"name" : "Shubham",
"userSettings" : {
"bike" : "pulsar"
}
}
Vì chúng ta trả về phản hồi trực tiếp từ MongoDB, kết quả nhận được sẽ có dạng như sau:
{
"userId": "5a5f28cc3178058b0fafe1dd",
"name": "Shubham",
"creationDate": 1516165830856,
"userSettings": {
"bike" : "pulsar"
}
}
Bạn có thể lấy danh sách tất cả người dùng bằng cách sử dụng API dưới dạng yêu cầu GET:
<https://localhost:8102/user/>
Chúng ta sẽ nhận được kết quả tương tự như sau:
[
{
"userId": "5a5f28cc3178058b0fafe1dd",
"name": "Shubham",
"creationDate": 1516165830856,
"userSettings": {
"bike" : "pulsar"
}
}
]
Nếu bạn để ý lớp UserController ở trên, chúng ta chưa kết nối MongoTemplate để sử dụng. Đoạn mã dưới đây minh họa các thay đổi cần thiết để dùng MongoTemplate trong việc đọc cài đặt người dùng.
//define Data Access Layer object
private final UserDAL userDAL;
//initialize DAL object via constructor autowiring
public UserController(UserRepository userRepository, UserDAL userDAL) {
this.userRepository = userRepository;
this.userDAL = userDAL;
}
//change method implementation to use DAL and hence MongoTemplate
@RequestMapping(value = "/settings/{userId}", method = RequestMethod.GET)
public Object getAllUserSettings(@PathVariable String userId) {
User user = userRepository.findOne(userId);
if (user != null) {
return userDAL.getAllUserSettings(userId);
} else {
return "User not found.";
}
}
//change method implementation to use DAL and hence MongoTemplate
@RequestMapping(value = "/settings/{userId}/{key}", method = RequestMethod.GET)
public String getUserSetting(
@PathVariable String userId, @PathVariable String key) {
return userDAL.getUserSetting(userId, key);
}
Khởi động lại ứng dụng và chạy các trường hợp để lấy toàn bộ cài đặt người dùng cũng như lấy một khóa cụ thể. Hình ảnh dưới đây là kết quả trả về từ ứng dụng Postman.


MongoTemplate và MongoRepository
MongoTemplate cung cấp nhiều quyền kiểm soát hơn khi thực hiện truy vấn dữ liệu và lựa chọn dữ liệu cần lấy từ cơ sở dữ liệu.
Các repository của Spring Data mang lại cho chúng ta một cách tiếp cận thuận tiện để truy xuất dữ liệu.
Tuy nhiên, MongoTemplate phụ thuộc chặt chẽ vào cơ sở dữ liệu cụ thể. Điều này có nghĩa là với các repository của Spring Data, bạn có thể dễ dàng chuyển sang sử dụng cơ sở dữ liệu khác như MySQL, Neo4J hoặc các hệ quản trị khác chỉ bằng cách thay đổi repository tương ứng của Spring Data. Điều này không khả thi khi sử dụng MongoTemplate.
Tóm tắt Spring Boot MongoDB
Trong bài học này, chúng ta đã tìm hiểu cách MongoTemplate giúp cung cấp nhiều quyền kiểm soát hơn so với các repository của Spring Data, nhưng đồng thời cũng có thể phức tạp hơn khi thực hiện các truy vấn sâu. Vì vậy, việc lựa chọn sử dụng phương pháp nào hoàn toàn phụ thuộc vào bạn khi phát triển ý tưởng của mình. Hãy để lại bình luận bên dưới nếu bạn có bất kỳ câu hỏi nào hoặc muốn trao đổi thêm nhé!