SpringBoot整合MongoDB,性能提升,优化实践

大家好,我是小悟。

一、MongoDB简介

MongoDB是一个基于分布式文件存储的NoSQL数据库,具有以下特点:

  1. 文档型数据库:数据以BSON(Binary JSON)格式存储,结构灵活
  2. 无模式设计:集合中的文档可以有不同的字段,无需预先定义表结构
  3. 高性能:支持索引、聚合管道、丰富的查询语言
  4. 高可用性:通过副本集实现数据冗余和故障转移
  5. 水平扩展:通过分片集群实现数据水平拆分
  6. 丰富的查询功能:支持丰富的查询操作符和聚合框架

二、整合步骤与代码实现

1. 环境准备

pom.xml 依赖配置:

xml 复制代码
<dependencies>
    <!-- Spring Boot Starter Data MongoDB -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Web (可选,用于REST API) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Lombok (简化代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

application.yml 配置:

yaml 复制代码
spring:
  data:
    mongodb:
      # 连接字符串格式
      uri: mongodb://localhost:27017/testdb
      
      # 或者分开配置
      # host: localhost
      # port: 27017
      # database: testdb
      # username: user
      # password: pass
      
      # 连接池配置
      auto-index-creation: true
      
# 自定义配置
mongodb:
  collection:
    user: users
    product: products

2. 实体类定义

kotlin 复制代码
package com.example.mongodemo.entity;

import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Builder;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 用户实体类
 * @Document 指定MongoDB集合名称
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "users") // 对应集合名
public class User {
    
    @Id // MongoDB主键
    private String id;
    
    @Field("username") // 指定字段名,可选
    private String username;
    
    private String email;
    
    private Integer age;
    
    private Address address; // 嵌套文档
    
    private List<String> hobbies; // 数组类型
    
    private LocalDateTime createTime;
    
    @Field("is_active")
    private Boolean isActive;
    
    /**
     * 嵌套文档示例
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Address {
        private String city;
        private String street;
        private String zipCode;
    }
}

3. Repository接口

java 复制代码
package com.example.mongodemo.repository;

import com.example.mongodemo.entity.User;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;

/**
 * 用户Repository
 * 继承MongoRepository获得CRUD基本操作
 */
@Repository
public interface UserRepository extends MongoRepository<User, String> {
    
    // 自定义查询方法 - 根据方法名自动生成查询
    List<User> findByUsername(String username);
    
    List<User> findByAgeGreaterThan(int age);
    
    List<User> findByEmailContaining(String domain);
    
    Optional<User> findByUsernameAndEmail(String username, String email);
    
    long countByIsActiveTrue();
    
    // 使用@Query注解自定义查询
    @Query("{ 'age' : { $gt: ?0, $lt: ?1 } }")
    List<User> findUsersByAgeBetween(int min, int max);
    
    @Query("{ 'address.city' : ?0 }")
    List<User> findByCity(String city);
    
    @Query("{ 'hobbies' : { $in: ?0 } }")
    List<User> findByHobbiesIn(List<String> hobbies);
    
    // 使用正则表达式查询
    @Query("{ 'email' : { $regex: ?0 } }")
    List<User> findByEmailPattern(String pattern);
}

4. 自定义Repository实现

java 复制代码
package com.example.mongodemo.repository.custom;

import com.example.mongodemo.entity.User;
import java.util.List;

public interface CustomUserRepository {
    List<User> findActiveUsersWithCustomQuery();
    void updateUserEmail(String userId, String newEmail);
}

package com.example.mongodemo.repository.custom.impl;

import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.custom.CustomUserRepository;
import lombok.RequiredArgsConstructor;
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.Repository;
import java.util.List;

@Repository
@RequiredArgsConstructor
public class CustomUserRepositoryImpl implements CustomUserRepository {
    
    private final MongoTemplate mongoTemplate;
    
    @Override
    public List<User> findActiveUsersWithCustomQuery() {
        Query query = new Query();
        query.addCriteria(Criteria.where("isActive").is(true))
             .addCriteria(Criteria.where("age").gte(18));
        return mongoTemplate.find(query, User.class);
    }
    
    @Override
    public void updateUserEmail(String userId, String newEmail) {
        Query query = new Query(Criteria.where("id").is(userId));
        Update update = new Update();
        update.set("email", newEmail);
        mongoTemplate.updateFirst(query, update, User.class);
    }
}

5. 更新主Repository接口

kotlin 复制代码
package com.example.mongodemo.repository;

import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.custom.CustomUserRepository;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends MongoRepository<User, String>, CustomUserRepository {
    // 方法继承自两个接口
}

6. Service层实现

arduino 复制代码
package com.example.mongodemo.service;

import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {
    
    private final UserRepository userRepository;
    
    /**
     * 创建用户
     */
    public User createUser(User user) {
        user.setCreateTime(LocalDateTime.now());
        if (user.getIsActive() == null) {
            user.setIsActive(true);
        }
        return userRepository.save(user);
    }
    
    /**
     * 批量插入用户
     */
    public List<User> batchCreateUsers(List<User> users) {
        users.forEach(user -> {
            user.setCreateTime(LocalDateTime.now());
            if (user.getIsActive() == null) {
                user.setIsActive(true);
            }
        });
        return userRepository.saveAll(users);
    }
    
    /**
     * 根据ID查询用户
     */
    public Optional<User> getUserById(String id) {
        return userRepository.findById(id);
    }
    
    /**
     * 查询所有活跃用户
     */
    public List<User> getActiveUsers() {
        return userRepository.findByIsActiveTrue();
    }
    
    /**
     * 分页查询用户
     */
    public Page<User> getUsersByPage(Pageable pageable) {
        return userRepository.findAll(pageable);
    }
    
    /**
     * 根据用户名和邮箱查询
     */
    public Optional<User> getUserByUsernameAndEmail(String username, String email) {
        return userRepository.findByUsernameAndEmail(username, email);
    }
    
    /**
     * 更新用户邮箱
     */
    public boolean updateUserEmail(String userId, String newEmail) {
        Optional<User> userOpt = userRepository.findById(userId);
        if (userOpt.isPresent()) {
            User user = userOpt.get();
            user.setEmail(newEmail);
            userRepository.save(user);
            return true;
        }
        return false;
    }
    
    /**
     * 使用自定义查询更新邮箱
     */
    public void updateUserEmailWithCustom(String userId, String newEmail) {
        userRepository.updateUserEmail(userId, newEmail);
    }
    
    /**
     * 删除用户
     */
    public boolean deleteUser(String id) {
        if (userRepository.existsById(id)) {
            userRepository.deleteById(id);
            return true;
        }
        return false;
    }
    
    /**
     * 根据年龄范围查询用户
     */
    public List<User> findUsersByAgeRange(int minAge, int maxAge) {
        return userRepository.findUsersByAgeBetween(minAge, maxAge);
    }
    
    /**
     * 统计活跃用户数量
     */
    public long countActiveUsers() {
        return userRepository.countByIsActiveTrue();
    }
}

7. Controller层

less 复制代码
package com.example.mongodemo.controller;

import com.example.mongodemo.entity.User;
import com.example.mongodemo.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
    
    private final UserService userService;
    
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userService.createUser(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
    }
    
    @PostMapping("/batch")
    public ResponseEntity<List<User>> batchCreateUsers(@RequestBody List<User> users) {
        List<User> savedUsers = userService.batchCreateUsers(users);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedUsers);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable String id) {
        Optional<User> user = userService.getUserById(id);
        return user.map(ResponseEntity::ok)
                  .orElse(ResponseEntity.notFound().build());
    }
    
    @GetMapping
    public ResponseEntity<Page<User>> getAllUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(defaultValue = "createTime") String sortBy) {
        
        Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).descending());
        Page<User> users = userService.getUsersByPage(pageable);
        return ResponseEntity.ok(users);
    }
    
    @GetMapping("/active")
    public ResponseEntity<List<User>> getActiveUsers() {
        List<User> activeUsers = userService.getActiveUsers();
        return ResponseEntity.ok(activeUsers);
    }
    
    @GetMapping("/age-range")
    public ResponseEntity<List<User>> getUsersByAgeRange(
            @RequestParam int minAge,
            @RequestParam int maxAge) {
        List<User> users = userService.findUsersByAgeRange(minAge, maxAge);
        return ResponseEntity.ok(users);
    }
    
    @PutMapping("/{id}/email")
    public ResponseEntity<Void> updateEmail(
            @PathVariable String id,
            @RequestParam String newEmail) {
        
        boolean updated = userService.updateUserEmail(id, newEmail);
        return updated ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable String id) {
        boolean deleted = userService.deleteUser(id);
        return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build();
    }
    
    @GetMapping("/stats/active-count")
    public ResponseEntity<Long> getActiveUserCount() {
        long count = userService.countActiveUsers();
        return ResponseEntity.ok(count);
    }
}

8. 聚合查询示例

typescript 复制代码
package com.example.mongodemo.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserAggregationService {
    
    private final MongoTemplate mongoTemplate;
    
    /**
     * 按城市分组统计用户数量
     */
    public List<UserCityStats> groupUsersByCity() {
        GroupOperation groupByCity = Aggregation.group("address.city")
                .count().as("userCount")
                .avg("age").as("averageAge");
        
        Aggregation aggregation = Aggregation.newAggregation(groupByCity);
        
        AggregationResults<UserCityStats> results = 
                mongoTemplate.aggregate(aggregation, "users", UserCityStats.class);
        
        return results.getMappedResults();
    }
    
    /**
     * 统计各年龄段用户数量
     */
    public List<AgeGroupStats> groupUsersByAgeGroup() {
        GroupOperation groupByAgeGroup = Aggregation.group(
                Aggregation.function("cond",
                        Aggregation.function("and",
                                Aggregation.function("gte", "age", 0),
                                Aggregation.function("lte", "age", 18)
                        ),
                        "0-18",
                        Aggregation.function("cond",
                                Aggregation.function("and",
                                        Aggregation.function("gt", "age", 18),
                                        Aggregation.function("lte", "age", 30)
                                ),
                                "19-30",
                                "30+"
                        )
                )
        ).count().as("count");
        
        Aggregation aggregation = Aggregation.newAggregation(groupByAgeGroup);
        
        AggregationResults<AgeGroupStats> results = 
                mongoTemplate.aggregate(aggregation, "users", AgeGroupStats.class);
        
        return results.getMappedResults();
    }
    
    // 统计结果类
    public static class UserCityStats {
        private String city;
        private Long userCount;
        private Double averageAge;
        // getters and setters
    }
    
    public static class AgeGroupStats {
        private String ageGroup;
        private Long count;
        // getters and setters
    }
}

9. 事务支持(需要MongoDB副本集)

java 复制代码
package com.example.mongodemo.service;

import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class TransactionalUserService {
    
    private final UserRepository userRepository;
    
    /**
     * 事务操作示例
     * 注意:MongoDB事务需要副本集
     */
    @Transactional(rollbackFor = Exception.class)
    public void createUserWithTransaction(User user1, User user2) {
        // 保存第一个用户
        user1.setCreateTime(LocalDateTime.now());
        user1.setIsActive(true);
        userRepository.save(user1);
        
        // 模拟业务逻辑
        if (user1.getEmail() == null) {
            throw new RuntimeException("Email is required");
        }
        
        // 保存第二个用户
        user2.setCreateTime(LocalDateTime.now());
        user2.setIsActive(true);
        userRepository.save(user2);
    }
}

10. 配置类

kotlin 复制代码
package com.example.mongodemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

@Configuration
@EnableMongoRepositories(basePackages = "com.example.mongodemo.repository")
@EnableMongoAuditing // 启用审计功能
public class MongoConfig {
    
    // 可以配置MongoTemplate的自定义设置
    // @Bean
    // public MongoTemplate mongoTemplate(MongoDatabaseFactory factory) {
    //     return new MongoTemplate(factory);
    // }
}

11. 审计功能(自动填充创建/更新时间)

kotlin 复制代码
package com.example.mongodemo.entity;

import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Document;
import java.time.LocalDateTime;

@Data
@Document(collection = "auditable_entities")
public class AuditableEntity {
    
    private String id;
    private String name;
    
    @CreatedDate
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime updatedAt;
}

12. 测试类

scss 复制代码
package com.example.mongodemo;

import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
class MongoDemoApplicationTests {
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void testCRUDOperations() {
        // 创建用户
        User user = User.builder()
                .username("testuser")
                .email("test@example.com")
                .age(25)
                .isActive(true)
                .hobbies(Arrays.asList("reading", "sports"))
                .build();
        
        // 保存
        User savedUser = userRepository.save(user);
        assertThat(savedUser.getId()).isNotNull();
        
        // 查询
        List<User> users = userRepository.findByUsername("testuser");
        assertThat(users).hasSize(1);
        
        // 更新
        savedUser.setEmail("updated@example.com");
        userRepository.save(savedUser);
        
        // 删除
        userRepository.delete(savedUser);
        
        // 验证删除
        long count = userRepository.count();
        assertThat(count).isEqualTo(0);
    }
    
    @Test
    void testPagination() {
        // 创建测试数据
        for (int i = 0; i < 15; i++) {
            User user = User.builder()
                    .username("user" + i)
                    .email("user" + i + "@example.com")
                    .age(20 + i)
                    .isActive(true)
                    .build();
            userRepository.save(user);
        }
        
        // 分页查询
        Page<User> page = userRepository.findAll(PageRequest.of(0, 10));
        assertThat(page.getContent()).hasSize(10);
        assertThat(page.getTotalElements()).isEqualTo(15);
    }
}

三、总结

1. 整合优势

Spring Data MongoDB提供了:

  • 简化的操作:通过Repository接口减少大量模板代码
  • 强大的查询:支持方法名查询、@Query注解、Criteria API
  • 对象映射:自动将文档映射为Java对象
  • 事务支持:支持MongoDB 4.0+的事务功能
  • 聚合框架:支持复杂的聚合操作
  • 审计功能:自动填充创建/更新时间

2. 最佳实践

  1. 连接配置:使用连接池提高性能,合理设置连接参数
  2. 索引优化:为频繁查询的字段创建索引
  3. 文档设计:合理设计文档结构,避免过度嵌套
  4. 批量操作:使用批量操作提高数据插入/更新效率
  5. 读写分离:考虑使用副本集实现读写分离

3. 注意事项

  1. 事务要求:MongoDB事务需要副本集环境
  2. 内存管理:注意大文档的内存消耗
  3. 连接管理:合理配置连接池大小
  4. 数据建模:根据查询模式设计文档结构
  5. 索引策略:避免过多的索引影响写入性能

4. 适用场景

  • 内容管理系统:文章、评论等半结构化数据
  • 物联网应用:设备日志、传感器数据
  • 实时分析:用户行为分析、实时统计
  • 社交应用:用户动态、消息数据
  • 电商平台:商品信息、用户收藏

SpringBoot与MongoDB的整合提供了高效、灵活的数据存储方案,特别适合处理半结构化、快速增长的数据场景。通过Spring Data MongoDB的封装,可以像操作传统关系型数据库一样操作MongoDB,同时享受NoSQL数据库的灵活性和扩展性优势。

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关推荐
No芒柠Exception2 小时前
从开发到上线的CI/CD 完整流程
后端·面试·架构
1***35772 小时前
spring loC&DI 详解
java·spring·rpc
独自破碎E2 小时前
介绍一下Spring AI框架
java·人工智能·spring
黎雁·泠崖2 小时前
Java入门篇之吃透基础语法(一):注释+关键字+字面量全解析
java·开发语言·intellij-idea·intellij idea
mjhcsp2 小时前
C++ 后缀树(Suffix Tree):原理、实现与应用全解析
java·开发语言·c++·suffix-tree·后缀树
天若有情6732 小时前
Spring Boot 前后端联调3大经典案例:从入门到实战(通俗易懂版)
spring boot·后端·状态模式
sin22012 小时前
Spring事务管理(SpringBoot)
java·spring boot·spring
C***11502 小时前
Spring TransactionTemplate 深入解析与高级用法
java·数据库·spring
BD_Marathon2 小时前
SpringBoot——配置文件格式
java·spring boot·后端