Project Dependencies
To begin, configure the pom.xml file with the necessary dependencies for Spring Boot, MongoDB integration, and Lombok to reduce boilerplate code.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Database Configuration
Configure the MongoDB connection settings in the application.yml file. This example connects to a local instance on the default port.
server:
port: 8080
spring:
application:
name: mongo-demo-service
data:
mongodb:
host: 127.0.0.1
database: user_db
Application Entry Point
The standard Spring Boot main class to bootstrap the application.
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MongoApplication {
public static void main(String[] args) {
SpringApplication.run(MongoApplication.class, args);
}
}
Data Model
Define the entity class UserProfile. This class represents a document in the MongoDB collection. Lombok annotations are used to generate getters and setters.
package com.example.demo.model;
import lombok.Data;
import org.springframework.data.annotation.Id;
@Data
public class UserProfile {
@Id
private String id;
private String username;
private Integer age;
}
Repository Layer
Create the repository interface by extending MongoRepository. A custom query method is defined to search for users by name pattern and age range with pagination support.
package com.example.demo.repository;
import com.example.demo.model.UserProfile;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository<UserProfile, String> {
Page<UserProfile> findByUsernameContainingAndAgeBetween(String name, Integer minAge, Integer maxAge, Pageable pageable);
}
Service Layer
The service layer handles business logic. It utilizes both MongoRepository for standard CRUD and MongoTemplate for more complex operations, such as atomic field increments.
package com.example.demo.service;
import com.example.demo.model.UserProfile;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
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.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private MongoTemplate mongoTemplate;
public Page<UserProfile> searchUsers(String name, Integer min, Integer max, Pageable pageable) {
return userRepository.findByUsernameContainingAndAgeBetween(name, min, max, pageable);
}
public UserProfile getUserById(String id) {
return userRepository.findById(id).orElse(null);
}
public void createUser(UserProfile user) {
userRepository.save(user);
}
public void updateUser(UserProfile user) {
userRepository.save(user);
}
public void incrementUserAge(String userId) {
Query searchQuery = new Query(Criteria.where("id").is(userId));
Update modification = new Update().inc("age", 1);
mongoTemplate.updateFirst(searchQuery, modification, UserProfile.class);
}
public void deleteUser(String id) {
userRepository.deleteById(id);
}
}
Controller Layer
The REST controller exposes endpoints for client interaction. It uses a generic wrapper class ApiResponse for consistent JSON responses.
package com.example.demo.controller;
import com.example.demo.model.UserProfile;
import com.example.demo.service.UserService;
import com.example.demo.util.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/api/users")
@CrossOrigin
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/search")
public ApiResponse search(@RequestBody Map<String, Object> payload) {
int pageNum = (Integer) payload.getOrDefault("pageNum", 1);
int pageSize = (Integer) payload.getOrDefault("pageSize", 10);
PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize);
String name = (String) payload.getOrDefault("username", "");
Integer minAge = (Integer) payload.getOrDefault("minAge", 0);
Integer maxAge = (Integer) payload.getOrDefault("maxAge", 120);
Page<UserProfile> result = userService.searchUsers(name, minAge, maxAge, pageRequest);
return ApiResponse.success(result);
}
@GetMapping("/{id}")
public ApiResponse getById(@PathVariable String id) {
return ApiResponse.success(userService.getUserById(id));
}
@PostMapping("/save")
public ApiResponse save(@RequestBody UserProfile user) {
user.setId(UUID.randomUUID().toString());
userService.createUser(user);
return ApiResponse.success("User created successfully");
}
@PostMapping("/update")
public ApiResponse update(@RequestBody UserProfile user) {
userService.updateUser(user);
return ApiResponse.success("User updated successfully");
}
@PostMapping("/incrementAge/{id}")
public ApiResponse incrementAge(@PathVariable String id) {
userService.incrementUserAge(id);
return ApiResponse.success("Age updated successfully");
}
@DeleteMapping("/delete/{id}")
public ApiResponse delete(@PathVariable String id) {
userService.deleteUser(id);
return ApiResponse.success("User deleted successfully");
}
}
Response Wrapper Utility
A utility class to standardize the API response structure, including status codes, messages, and data payload.
package com.example.demo.util;
import java.util.HashMap;
import java.util.Map;
public class ApiResponse {
private int code;
private String message;
private Map<String, Object> data = new HashMap<>();
public static ApiResponse success() {
ApiResponse response = new ApiResponse();
response.code = 200;
response.message = "Success";
return response;
}
public static ApiResponse success(Object payload) {
ApiResponse response = success();
response.data.put("result", payload);
return response;
}
public static ApiResponse fail() {
ApiResponse response = new ApiResponse();
response.code = 400;
response.message = "Error";
return response;
}
// Getters and Setters
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Map<String, Object> getData() { return data; }
public void setData(Map<String, Object> data) { this.data = data; }
}