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数据库的灵活性和扩展性优势。

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

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

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

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

相关推荐
大傻^7 小时前
LangChain4j Spring Boot Starter:自动配置与声明式 Bean 管理
java·人工智能·spring boot·spring·langchain4j
沐硕7 小时前
《基于改进协同过滤与多目标优化的健康饮食推荐系统设计与实现》
java·python·算法·fastapi·多目标优化·饮食推荐·改进协同过滤
yhole7 小时前
springboot 修复 Spring Framework 特定条件下目录遍历漏洞(CVE-2024-38819)
spring boot·后端·spring
BingoGo8 小时前
Laravel 13 正式发布 使用 Laravel AI 无缝平滑升级
后端·php
愣头不青8 小时前
560.和为k的子数组
java·数据结构
共享家95278 小时前
Java入门(String类)
java·开发语言
l软件定制开发工作室8 小时前
Spring开发系列教程(34)——打包Spring Boot应用
java·spring boot·后端·spring·springboot
0xDevNull8 小时前
Spring Boot 循环依赖解决方案完全指南
java·开发语言·spring
爱丽_8 小时前
GC 怎么判定“该回收谁”:GC Roots、可达性分析、四种引用与回收算法
java·jvm·算法
bbq粉刷匠8 小时前
Java--多线程--单例模式
java·开发语言·单例模式