SpringBoot整合MongoDB的完整操作指南
依賴配置
在 pom.xml 中添加以下依賴:
<dependencies>
<!-- SpringBoot Starter MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Lombok (可選,簡化實體類) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Hutool工具包 (可選) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
</dependencies>
配置文件
在 application.yml 中配置MongoDB連接信息:
基礎(chǔ)配置(使用URI方式)
spring:
data:
mongodb:
# MongoDB連接URI
# 格式:mongodb://[username:password@]host:port/database
# 示例說明:
# - mongodb://localhost:27017:本地MongoDB,默認(rèn)端口27017
# - mongodb://admin:123456@localhost:27017:帶用戶名密碼的連接
# - mongodb://host1:27017,host2:27017:連接副本集
uri: mongodb://localhost:27017
# 數(shù)據(jù)庫名稱
# 說明:指定要使用的MongoDB數(shù)據(jù)庫
# 注意:數(shù)據(jù)庫不存在時會自動創(chuàng)建
database: my_database
# 自動創(chuàng)建索引
# 說明:啟動時自動為實體類上的@Indexed注解創(chuàng)建索引
# 優(yōu)點:開發(fā)環(huán)境方便,自動創(chuàng)建索引
# 缺點:生產(chǎn)環(huán)境可能影響啟動速度
# 建議:生產(chǎn)環(huán)境設(shè)置為false,手動管理索引
auto-index-creation: true
完整配置(詳細(xì)配置方式)
spring:
data:
mongodb:
# ==================== 連接基礎(chǔ)配置 ====================
# MongoDB服務(wù)器地址
# 說明:可以是IP地址或域名
# 示例:
# - localhost:本地連接
# - 192.168.1.100:指定IP地址
# - mongodb.example.com:域名連接
host: localhost
# MongoDB服務(wù)器端口
# 說明:MongoDB默認(rèn)端口為27017
# 注意:確保端口沒有被防火墻阻止
port: 27017
# 數(shù)據(jù)庫名稱
# 說明:指定要連接的數(shù)據(jù)庫名稱
database: my_database
# ==================== 認(rèn)證配置 ====================
# 用戶名
# 說明:如果MongoDB啟用了認(rèn)證,需要提供用戶名
# 注意:用戶必須有對應(yīng)數(shù)據(jù)庫的訪問權(quán)限
# 示例:admin用戶通常是管理員賬戶
username: admin
# 密碼
# 說明:對應(yīng)用戶名的密碼
# 安全建議:
# - 生產(chǎn)環(huán)境不要明文存儲密碼
# - 建議使用環(huán)境變量或加密配置
# - 示例:${MONGODB_PASSWORD:admin123}
password: admin123
# 認(rèn)證數(shù)據(jù)庫
# 說明:用戶認(rèn)證信息存儲的數(shù)據(jù)庫
# 默認(rèn)值:admin
# 注意:大多數(shù)用戶的認(rèn)證信息存儲在admin數(shù)據(jù)庫中
authentication-database: admin
# ==================== 索引配置 ====================
# 自動創(chuàng)建索引
# 說明:應(yīng)用啟動時自動創(chuàng)建索引
# 開發(fā)環(huán)境:建議設(shè)置為true,方便開發(fā)
# 生產(chǎn)環(huán)境:建議設(shè)置為false,手動管理索引更安全
# 原因:
# - 生產(chǎn)環(huán)境索引創(chuàng)建可能影響性能
# - 可以提前規(guī)劃好索引,避免運(yùn)行時創(chuàng)建
auto-index-creation: true
# ==================== 連接池配置 ====================
# 每個主機(jī)最小連接數(shù)
# 說明:連接池中保持的最小連接數(shù)量
# 作用:避免頻繁創(chuàng)建和銷毀連接的開銷
# 建議:
# - 低負(fù)載應(yīng)用:5-10
# - 中等負(fù)載應(yīng)用:10-20
# - 高負(fù)載應(yīng)用:20-50
min-connections-per-host: 10
# 每個主機(jī)最大連接數(shù)
# 說明:連接池中允許的最大連接數(shù)量
# 作用:限制并發(fā)連接數(shù),防止資源耗盡
# 建議:
# - 根據(jù)應(yīng)用并發(fā)量設(shè)置
# - 一般設(shè)置為CPU核心數(shù)的2-4倍
# - 例如:8核CPU可以設(shè)置為32-100
max-connections-per-host: 100
# 連接線程阻塞倍數(shù)
# 說明:允許等待連接的最大線程數(shù)
# 計算公式:max-connections-per-host × multiplier
# 示例:100 × 5 = 500個線程可以等待連接
# 作用:當(dāng)連接池耗盡時,允許一定數(shù)量的線程等待
# 建議:一般設(shè)置為3-5倍
threads-allowed-to-block-for-connection-multiplier: 5
# 連接超時時間(毫秒)
# 說明:獲取連接的最大等待時間
# 默認(rèn)值:10000毫秒(10秒)
# 作用:防止線程無限等待連接
# 建議:
# - 開發(fā)環(huán)境:5000-10000毫秒
# - 生產(chǎn)環(huán)境:根據(jù)響應(yīng)時間要求調(diào)整
# - 網(wǎng)絡(luò)不穩(wěn)定時可以適當(dāng)增加
connection-timeout: 10000
# Socket超時時間(毫秒)
# 說明:Socket讀寫操作的超時時間
# 默認(rèn)值:0(表示無超時,永久等待)
# 作用:防止網(wǎng)絡(luò)故障導(dǎo)致請求掛起
# 建議:
# - 簡單查詢:30000-60000毫秒(30-60秒)
# - 復(fù)雜查詢:120000毫秒(120秒)或更長
# - 注意:超時時間要根據(jù)實際業(yè)務(wù)需求設(shè)置
socket-timeout: 60000
# ==================== 其他可選配置 ====================
# 編解碼器(可選)
# 說明:指定MongoDB驅(qū)動使用的編解碼器
# 默認(rèn)值:org.bson.codecs.DocumentCodec
# 用途:自定義文檔的編碼解碼邏輯
# codec: com.example.mongodb.CustomCodec
# UUID表示方式(可選)
# 說明:UUID類型在MongoDB中的存儲格式
# 可選值:
# - JAVA_LEGACY:Java的傳統(tǒng)格式(3.0之前)
# - STANDARD:標(biāo)準(zhǔn)格式(推薦)
# - C_SHARP_LEGACY:C#的傳統(tǒng)格式
# - PYTHON_LEGACY:Python的傳統(tǒng)格式
# uuid-representation: STANDARD
# 副本集名稱(可選)
# 說明:連接副本集時指定的副本集名稱
# 用途:連接副本集時必須指定
# 示例:replica-set-name
# replica-set-name: myReplicaSet
# 讀取偏好(可選)
# 說明:指定讀取數(shù)據(jù)的首選節(jié)點
# 可選值:
# - primary:只從主節(jié)點讀?。J(rèn))
# - primaryPreferred:優(yōu)先從主節(jié)點讀取
# - secondary:只從從節(jié)點讀取
# - secondaryPreferred:優(yōu)先從從節(jié)點讀取
# - nearest:從最近節(jié)點讀取
# read-preference: secondaryPreferred
# 寫關(guān)注級別(可選)
# 說明:指定寫入操作的安全級別
# 可選值:
# - ACKNOWLEDGED:默認(rèn),確認(rèn)寫入主節(jié)點
# - W1:確認(rèn)寫入主節(jié)點和一個從節(jié)點
# - W2:確認(rèn)寫入主節(jié)點和兩個從節(jié)點
# - MAJORITY:確認(rèn)寫入大多數(shù)節(jié)點
# - UNACKNOWLEDGED:不等待確認(rèn),性能最好但不安全
# write-concern: ACKNOWLEDGED
多環(huán)境配置示例
開發(fā)環(huán)境配置(application-dev.yml)
spring:
data:
mongodb:
host: localhost
port: 27017
database: dev_database
username: dev_user
password: dev_password
auto-index-creation: true
# 開發(fā)環(huán)境連接池配置較小
min-connections-per-host: 5
max-connections-per-host: 20
connection-timeout: 10000
socket-timeout: 30000
測試環(huán)境配置(application-test.yml)
spring:
data:
mongodb:
host: test-mongodb.example.com
port: 27017
database: test_database
username: test_user
password: ${MONGODB_TEST_PASSWORD}
auto-index-creation: false
# 測試環(huán)境連接池配置中等
min-connections-per-host: 10
max-connections-per-host: 50
connection-timeout: 10000
socket-timeout: 60000
生產(chǎn)環(huán)境配置(application-prod.yml)
spring:
data:
mongodb:
# 生產(chǎn)環(huán)境使用URI方式連接副本集
uri: mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@mongo1.example.com:27017,mongo2.example.com:27017,mongo3.example.com:27017/${MONGODB_DATABASE}?replicaSet=prodReplicaSet&connectTimeoutMS=10000&socketTimeoutMS=60000
auto-index-creation: false
# 生產(chǎn)環(huán)境連接池配置較大
min-connections-per-host: 20
max-connections-per-host: 100
threads-allowed-to-block-for-connection-multiplier: 5
connection-timeout: 10000
socket-timeout: 60000
# 生產(chǎn)環(huán)境使用副本集,配置讀取偏好和寫關(guān)注
# read-preference: secondaryPreferred
# write-concern: MAJORITY
配置參數(shù)詳解
連接池參數(shù)對比表
| 參數(shù) | 說明 | 推薦值(小應(yīng)用) | 推薦值(大應(yīng)用) | 調(diào)優(yōu)建議 |
|---|---|---|---|---|
| min-connections-per-host | 最小連接數(shù) | 5-10 | 20-50 | 根據(jù)平均并發(fā)量設(shè)置 |
| max-connections-per-host | 最大連接數(shù) | 20-50 | 100-200 | 根據(jù)峰值并發(fā)量設(shè)置 |
| threads-allowed-to-block-for-connection-multiplier | 阻塞倍數(shù) | 5 | 5 | 一般不需要調(diào)整 |
| connection-timeout | 連接超時(ms) | 10000 | 10000 | 網(wǎng)絡(luò)不穩(wěn)定時可增加 |
| socket-timeout | Socket超時(ms) | 30000-60000 | 60000-120000 | 根據(jù)查詢復(fù)雜度調(diào)整 |
常見問題與解決方案
1. 連接超時問題
現(xiàn)象:應(yīng)用啟動或查詢時頻繁出現(xiàn)連接超時
解決方案:
spring:
data:
mongodb:
# 增加連接超時時間
connection-timeout: 20000
# 增加Socket超時時間
socket-timeout: 120000
# 增加連接池大小
max-connections-per-host: 200
2. 副本集連接問題
現(xiàn)象:連接副本集時出現(xiàn)"not master and slaveOk=false"錯誤
解決方案:
spring:
data:
mongodb:
# 使用URI方式連接副本集
uri: mongodb://user:pass@host1:27017,host2:27017,host3:27017/database?replicaSet=myReplicaSet&readPreference=secondaryPreferred
3. 認(rèn)證失敗問題
現(xiàn)象:認(rèn)證失敗,無法連接數(shù)據(jù)庫
解決方案:
spring:
data:
mongodb:
username: your_username
password: your_password
authentication-database: admin # 確保指定正確的認(rèn)證數(shù)據(jù)庫
安全建議
密碼安全
- 不要在配置文件中明文存儲密碼
- 使用環(huán)境變量:
${MONGODB_PASSWORD} - 使用加密配置中心
網(wǎng)絡(luò)安全
- 生產(chǎn)環(huán)境不要使用localhost
- 使用內(nèi)網(wǎng)IP或VPN
- 配置MongoDB的防火墻規(guī)則
權(quán)限控制
- 為不同環(huán)境創(chuàng)建不同的數(shù)據(jù)庫用戶
- 遵循最小權(quán)限原則
- 定期更換密碼
核心工具類實現(xiàn)
1. 響應(yīng)結(jié)果封裝類
package com.example.mongodb.common;
import lombok.Data;
import java.io.Serializable;
/**
* 統(tǒng)一響應(yīng)結(jié)果封裝
*/
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Integer code;
private String message;
private T data;
private Long total;
public Result() {
}
public Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
public static <T> Result<T> success(String message, T data) {
return new Result<>(200, message, data);
}
public static <T> Result<T> error(String message) {
return new Result<>(500, message, null);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
public Result<T> total(Long total) {
this.total = total;
return this;
}
}
2. 分頁請求參數(shù)類
package com.example.mongodb.common;
import lombok.Data;
/**
* 分頁請求參數(shù)
*/
@Data
public class PageRequest {
/** 當(dāng)前頁碼 */
private Integer pageNum = 1;
/** 每頁條數(shù) */
private Integer pageSize = 10;
/** 排序字段 */
private String sortField;
/** 排序方式:asc/desc */
private String sortOrder = "asc";
public PageRequest() {
}
public PageRequest(Integer pageNum, Integer pageSize) {
this.pageNum = pageNum;
this.pageSize = pageSize;
}
public int getSkip() {
return (pageNum - 1) * pageSize;
}
}
3. 核心MongoDB工具類(詳細(xì)注釋版)
package com.example.mongodb.utils;
import com.example.mongodb.common.PageRequest;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
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.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.regex.Pattern;
/**
* MongoDB工具類 - 提供完整的CRUD操作和高級查詢功能
*
* ========================================
* 使用說明:
* ========================================
* 1. 使用泛型<T>支持任意實體類型
* 2. 基于MongoTemplate實現(xiàn),提供了豐富的封裝方法
* 3. 支持事務(wù)、聚合、批量操作等高級特性
* 4. 所有方法都有詳細(xì)注釋,方便理解和使用
*
* @author 作者
* @date 2026-02-09
*/
@Slf4j
@Component
public class MongoUtils<T> {
// ==================== 核心依賴注入 ====================
/**
* MongoDB操作模板
* 說明:Spring Data MongoDB的核心類,提供了對MongoDB數(shù)據(jù)庫的所有操作
* 使用場景:執(zhí)行查詢、插入、更新、刪除等所有數(shù)據(jù)庫操作
*/
@Autowired
private MongoTemplate mongoTemplate;
/**
* 獲取集合名稱
* 功能:根據(jù)實體類獲取對應(yīng)的MongoDB集合名稱(相當(dāng)于關(guān)系數(shù)據(jù)庫的表名)
* 實現(xiàn)原理:通過@Document注解的collection屬性,或使用類名小寫形式
*
* @param entityClass 實體類類型(帶@Document注解的類)
* @return 集合名稱(MongoDB中的集合名,如"user"、"order"等)
*/
private String getCollectionName(Class<?> entityClass) {
return mongoTemplate.getCollectionName(entityClass);
}
// ==================== 基礎(chǔ)CRUD操作 ====================
/**
* 保存單個實體
* 功能:保存或更新實體對象
*
* 執(zhí)行邏輯:
* 1. 如果實體對象包含_id字段且值不為空,則執(zhí)行更新操作(upsert)
* 2. 如果實體對象不包含_id或_id為空,則執(zhí)行插入操作
* 3. 插入成功后,MongoDB會自動生成ObjectId并賦值給_id字段
*
* 使用場景:
* - 新增數(shù)據(jù)時直接調(diào)用
* - 修改數(shù)據(jù)時傳入包含_id的對象
*
* @param entity 要保存的實體對象(必須包含@Document注解)
* @return 保存后的實體對象(包含生成的_id)
*/
public T save(T entity) {
return mongoTemplate.save(entity);
}
/**
* 批量保存實體
* 功能:一次性保存多個實體對象,提高批量插入性能
*
* 性能優(yōu)勢:
* - 相比循環(huán)調(diào)用save()方法,批量操作可以減少網(wǎng)絡(luò)往返次數(shù)
* - MongoDB支持批量插入,內(nèi)部優(yōu)化了寫入性能
*
* 注意事項:
* - 如果數(shù)據(jù)量很大(如超過10000條),建議分批插入
* - 如果其中某個文檔插入失敗,整個操作可能會回滾
*
* @param entities 實體集合(List、Set等Collection類型)
* @return 保存后的實體集合(包含生成的_id)
*/
public List<T> saveBatch(Collection<T> entities) {
return (List<T>) mongoTemplate.insertAll(entities);
}
/**
* 插入單個實體
* 功能:直接插入實體,不檢查是否已存在
*
* 與save()的區(qū)別:
* - save():智能判斷是插入還是更新(基于_id)
* - insert():強(qiáng)制插入,如果_id已存在會拋出DuplicateKeyException異常
*
* 使用場景:
* - 確保不會重復(fù)插入時使用
* - 需要明確區(qū)分新增和更新操作時使用
*
* @param entity 要插入的實體對象
* @return 插入后的實體對象
* @throws org.springframework.dao.DuplicateKeyException 如果_id已存在
*/
public T insert(T entity) {
return mongoTemplate.insert(entity);
}
/**
* 批量插入實體
* 功能:批量插入到指定集合中,適合大量數(shù)據(jù)初始化
*
* 使用說明:
* - 通過entityClass參數(shù)指定目標(biāo)集合
* - 相比saveBatch(),此方法可以明確指定集合名稱
*
* @param entities 實體集合
* @param entityClass 實體類類型(用于確定集合名稱)
* @return 插入后的實體集合
*/
public List<T> insertBatch(Collection<T> entities, Class<T> entityClass) {
return (List<T>) mongoTemplate.insert(entities, entityClass);
}
/**
* 根據(jù)ID查詢實體
* 功能:通過MongoDB的文檔ID(_id字段)精確查詢單個文檔
*
* 執(zhí)行邏輯:
* 1. 構(gòu)造查詢條件:WHERE _id = ?
* 2. 執(zhí)行查詢,返回匹配的第一個文檔
* 3. 將文檔映射為Java實體對象
*
* ID類型說明:
* - MongoDB的_id默認(rèn)類型是ObjectId(24位十六進(jìn)制字符串)
* - Spring Data MongoDB支持String、ObjectId、Long等多種類型
*
* @param id 實體ID(通常是ObjectId類型或字符串)
* @param entityClass 實體類類型(用于結(jié)果映射)
* @return 查詢到的實體對象,不存在則返回null
*/
public T findById(Object id, Class<T> entityClass) {
return mongoTemplate.findById(id, entityClass);
}
/**
* 查詢所有實體
* 功能:查詢集合中的所有文檔
*
* 性能警告:
* - ?? 如果集合中數(shù)據(jù)量很大,此方法可能導(dǎo)致內(nèi)存溢出
* - ?? 生產(chǎn)環(huán)境中謹(jǐn)慎使用,建議使用分頁查詢
*
* 使用場景:
* - 數(shù)據(jù)量小的配置表
* - 數(shù)據(jù)導(dǎo)入導(dǎo)出操作
*
* @param entityClass 實體類類型
* @return 所有實體的列表
*/
public List<T> findAll(Class<T> entityClass) {
return mongoTemplate.findAll(entityClass);
}
/**
* 根據(jù)條件查詢單個實體
* 功能:根據(jù)自定義查詢條件返回第一個匹配的文檔
*
* 執(zhí)行邏輯:
* 1. 執(zhí)行傳入的查詢條件
* 2. 只返回第一個匹配的文檔
* 3. 如果沒有匹配文檔,返回null
*
* 使用場景:
* - 根據(jù)唯一鍵查詢(如用戶名、手機(jī)號)
* - 獲取最新的一條記錄(配合排序)
*
* @param query 查詢條件對象(通過Criteria構(gòu)造)
* @param entityClass 實體類類型
* @return 查詢到的實體對象,不存在則返回null
*/
public T findOne(Query query, Class<T> entityClass) {
return mongoTemplate.findOne(query, entityClass);
}
/**
* 根據(jù)條件查詢實體列表
* 功能:根據(jù)自定義查詢條件返回所有匹配的文檔
*
* 執(zhí)行邏輯:
* 1. 執(zhí)行傳入的查詢條件
* 2. 返回所有匹配的文檔列表
* 3. 如果沒有匹配文檔,返回空列表(不會返回null)
*
* 使用場景:
* - 條件查詢(如查詢某個狀態(tài)的所有用戶)
* - 范圍查詢(如查詢某個時間段的所有訂單)
*
* @param query 查詢條件對象(可以通過Criteria添加多個條件)
* @param entityClass 實體類類型
* @return 匹配的實體列表(可能為空列表)
*/
public List<T> find(Query query, Class<T> entityClass) {
return mongoTemplate.find(query, entityClass);
}
/**
* 根據(jù)ID更新實體
* 功能:根據(jù)文檔ID更新指定字段,只更新傳入的字段
*
* 執(zhí)行邏輯:
* 1. 構(gòu)造查詢條件:WHERE _id = ?
* 2. 執(zhí)行更新操作,只修改Update對象中指定的字段
* 3. 其他字段保持不變
*
* 與save()的區(qū)別:
* - save():會替換整個文檔(未傳入的字段會被清空)
* - updateById():只更新指定字段(更安全,性能更好)
*
* @param id 文檔ID(MongoDB的_id字段值)
* @param update 更新操作對象(包含要更新的字段和新值)
* @param entityClass 實體類類型
* @return 更新結(jié)果對象,包含匹配數(shù)和修改數(shù)等信息
*/
public UpdateResult updateById(Object id, Update update, Class<T> entityClass) {
// 構(gòu)造查詢條件:ID等于傳入的id
Query query = new Query(Criteria.where("_id").is(id));
return mongoTemplate.updateFirst(query, update, entityClass);
}
/**
* 根據(jù)條件更新第一個匹配的文檔
* 功能:只更新查詢到的第一個文檔,即使有多個文檔匹配條件
*
* 執(zhí)行邏輯:
* 1. 根據(jù)query條件查找匹配的文檔
* 2. 只更新第一個匹配的文檔
* 3. 返回更新結(jié)果(包含影響的文檔數(shù))
*
* 使用場景:
* - 唯一鍵字段的更新
* - 只需要更新一條記錄的情況
*
* @param query 查詢條件對象(用于定位要更新的文檔)
* @param update 更新操作對象(包含要更新的字段和新值)
* @param entityClass 實體類類型
* @return 更新結(jié)果對象,包含修改的文檔數(shù)等信息
*/
public UpdateResult updateFirst(Query query, Update update, Class<T> entityClass) {
return mongoTemplate.updateFirst(query, update, entityClass);
}
/**
* 根據(jù)條件更新所有匹配的文檔
* 功能:更新查詢條件匹配的所有文檔
*
* 執(zhí)行邏輯:
* 1. 根據(jù)query條件查找所有匹配的文檔
* 2. 更新所有匹配的文檔
* 3. 返回更新結(jié)果(包含影響的文檔數(shù))
*
* 性能注意事項:
* - 如果匹配的文檔數(shù)量很多,此操作可能耗時較長
* - 建議在查詢條件中添加索引以提高性能
*
* 使用場景:
* - 批量修改(如將所有狀態(tài)為0的用戶改為1)
* - 數(shù)據(jù)修復(fù)和遷移
*
* @param query 查詢條件對象(用于定位要更新的文檔)
* @param update 更新操作對象(包含要更新的字段和新值)
* @param entityClass 實體類類型
* @return 更新結(jié)果對象,包含修改的文檔數(shù)等信息
*/
public UpdateResult updateMulti(Query query, Update update, Class<T> entityClass) {
return mongoTemplate.updateMulti(query, update, entityClass);
}
/**
* 根據(jù)ID刪除實體
* 功能:刪除指定ID的文檔
*
* 執(zhí)行邏輯:
* 1. 構(gòu)造查詢條件:WHERE _id = ?
* 2. 執(zhí)行刪除操作
* 3. 返回刪除結(jié)果(包含刪除的文檔數(shù))
*
* 使用場景:
* - 根據(jù)主鍵刪除單個文檔
* - 物理刪除數(shù)據(jù)
*
* @param id 文檔ID(MongoDB的_id字段值)
* @param entityClass 實體類類型
* @return 刪除結(jié)果對象,包含刪除的文檔數(shù)
*/
public DeleteResult deleteById(Object id, Class<T> entityClass) {
Query query = new Query(Criteria.where("_id").is(id));
return mongoTemplate.remove(query, entityClass);
}
/**
* 根據(jù)條件刪除實體
* 功能:刪除查詢條件匹配的所有文檔
*
* 執(zhí)行邏輯:
* 1. 根據(jù)query條件查找所有匹配的文檔
* 2. 刪除所有匹配的文檔
* 3. 返回刪除結(jié)果(包含刪除的文檔數(shù))
*
* ?? 安全警告:
* - 如果query為空或條件太寬泛,會刪除大量數(shù)據(jù)
* - 生產(chǎn)環(huán)境中務(wù)必仔細(xì)檢查查詢條件
*
* @param query 查詢條件對象(用于定位要刪除的文檔)
* @param entityClass 實體類類型
* @return 刪除結(jié)果對象,包含刪除的文檔數(shù)
*/
public DeleteResult delete(Query query, Class<T> entityClass) {
return mongoTemplate.remove(query, entityClass);
}
/**
* 刪除所有實體
* 功能:清空整個集合中的所有文檔
*
* ?? 危險操作:
* - 此操作會刪除集合中的所有文檔
* - 集合本身不會被刪除,但數(shù)據(jù)會全部清空
* - 生產(chǎn)環(huán)境中謹(jǐn)慎使用,建議先備份數(shù)據(jù)
*
* 使用場景:
* - 測試數(shù)據(jù)清理
* - 數(shù)據(jù)重置
*
* @param entityClass 實體類類型
* @return 刪除結(jié)果對象,包含刪除的文檔數(shù)
*/
public DeleteResult deleteAll(Class<T> entityClass) {
return mongoTemplate.remove(new Query(), entityClass);
}
/**
* 檢查實體是否存在
* 功能:檢查是否存在匹配查詢條件的文檔
*
* 執(zhí)行邏輯:
* 1. 根據(jù)query條件查詢
* 2. 如果至少存在一個匹配文檔,返回true
* 3. 如果沒有匹配文檔,返回false
*
* 性能優(yōu)勢:
* - 相比count(),此方法在找到第一個匹配文檔后立即返回
* - 對于判斷存在性的場景,性能更好
*
* 使用場景:
* - 檢查用戶名是否已存在
* - 檢查某個數(shù)據(jù)是否存在
*
* @param query 查詢條件對象
* @param entityClass 實體類類型
* @return true表示存在,false表示不存在
*/
public boolean exists(Query query, Class<T> entityClass) {
return mongoTemplate.exists(query, entityClass);
}
/**
* 統(tǒng)計文檔數(shù)量
* 功能:統(tǒng)計匹配查詢條件的文檔總數(shù)
*
* 執(zhí)行邏輯:
* 1. 根據(jù)query條件查詢
* 2. 統(tǒng)計所有匹配文檔的數(shù)量
* 3. 返回總數(shù)(long類型)
*
* 使用場景:
* - 分頁查詢時先統(tǒng)計總數(shù)
* - 數(shù)據(jù)統(tǒng)計報表
* - 數(shù)據(jù)質(zhì)量檢查
*
* 性能注意事項:
* - 在大表上執(zhí)行count()可能較慢
* - 建議為查詢條件添加索引
*
* @param query 查詢條件對象
* @param entityClass 實體類類型
* @return 文檔數(shù)量
*/
public long count(Query query, Class<T> entityClass) {
return mongoTemplate.count(query, entityClass);
}
// ==================== 分頁查詢 ====================
/**
* 分頁查詢
* 功能:支持排序和分頁的綜合查詢方法
*
* 執(zhí)行邏輯:
* 1. 如果指定了排序字段,添加排序條件
* 2. 先統(tǒng)計符合條件的總記錄數(shù)
* 3. 根據(jù)頁碼和每頁大小計算skip和limit
* 4. 執(zhí)行分頁查詢
* 5. 構(gòu)造Spring Data的Page對象返回
*
* 返回的Page對象包含:
* - content:當(dāng)前頁的數(shù)據(jù)列表
* - totalElements:總記錄數(shù)
* - totalPages:總頁數(shù)
* - currentPage:當(dāng)前頁碼
* - hasNext/hasPrevious:是否有下一頁/上一頁
*
* 性能優(yōu)化建議:
* - 為排序字段和查詢條件字段添加索引
* - 避免使用過大的skip值(深度分頁)
*
* @param query 查詢條件對象(可以為空,表示查詢所有)
* @param pageRequest 分頁參數(shù)對象(包含頁碼、每頁大小、排序信息)
* @param entityClass 實體類類型
* @return Spring Data的Page對象,包含數(shù)據(jù)和分頁信息
*/
public Page<T> findPage(Query query, PageRequest pageRequest, Class<T> entityClass) {
// 如果指定了排序字段,則添加排序條件
if (pageRequest.getSortField() != null) {
// 判斷排序方向:desc表示降序,否則為升序
Sort.Direction direction = "desc".equalsIgnoreCase(pageRequest.getSortOrder())
? Sort.Direction.DESC : Sort.Direction.ASC;
// 將排序條件添加到查詢對象中
query.with(Sort.by(direction, pageRequest.getSortField()));
}
// 先統(tǒng)計總數(shù),用于分頁信息
long total = mongoTemplate.count(query, entityClass);
// 設(shè)置分頁參數(shù):跳過前N條數(shù)據(jù),然后限制返回數(shù)量
// skip: 跳過的記錄數(shù) = (頁碼-1) * 每頁大小
// limit: 每頁返回的記錄數(shù)
query.skip(pageRequest.getSkip()).limit(pageRequest.getPageSize());
// 執(zhí)行分頁查詢
List<T> list = mongoTemplate.find(query, entityClass);
// 構(gòu)造Spring Data的Page對象返回
return new PageImpl<>(list,
PageRequest.of(pageRequest.getPageNum() - 1, pageRequest.getPageSize()),
total);
}
// ==================== 高級查詢操作 ====================
/**
* 模糊查詢(不區(qū)分大小寫)
* 功能:支持SQL LIKE '%keyword%'的模糊匹配,不區(qū)分大小寫
*
* 實現(xiàn)原理:
* 1. 使用正則表達(dá)式實現(xiàn)模糊匹配
* 2. Pattern.CASE_INSENSITIVE標(biāo)志表示不區(qū)分大小寫
* 3. MongoDB的$regex操作符支持正則表達(dá)式查詢
*
* 性能注意事項:
* - 模糊查詢無法使用普通索引,只能全表掃描
* - 在數(shù)據(jù)量大的情況下性能較差
* - 建議配合其他條件縮小查詢范圍
*
* 使用場景:
* - 搜索功能(如搜索用戶名、商品名)
* - 數(shù)據(jù)模糊匹配
*
* @param fieldName 字段名(要搜索的字段)
* @param keyword 關(guān)鍵詞(搜索的關(guān)鍵詞)
* @return 查詢條件對象(Criteria)
*/
public Criteria like(String fieldName, String keyword) {
// 使用正則表達(dá)式實現(xiàn)模糊匹配,CASE_INSENSITIVE表示不區(qū)分大小寫
Pattern pattern = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE);
return Criteria.where(fieldName).regex(pattern);
}
/**
* 多字段模糊查詢
* 功能:在多個字段中搜索關(guān)鍵詞,任意一個字段匹配即滿足條件
*
* 實現(xiàn)原理:
* 1. 為每個字段構(gòu)造一個模糊查詢條件
* 2. 使用OR操作符連接多個條件
* 3. MongoDB的$or操作符實現(xiàn)OR邏輯
*
* SQL等價:
* WHERE field1 LIKE '%keyword%' OR field2 LIKE '%keyword%' OR ...
*
* 使用場景:
* - 全局搜索(同時搜索標(biāo)題、內(nèi)容、作者等)
* - 多字段組合搜索
*
* @param fields 字段名數(shù)組(要在哪些字段中搜索)
* @param keyword 關(guān)鍵詞(搜索的關(guān)鍵詞)
* @return 查詢條件對象(Criteria,使用OR連接)
*/
public Criteria multiFieldLike(String[] fields, String keyword) {
// 創(chuàng)建查詢條件數(shù)組,每個字段一個條件
Criteria[] criteriaArray = new Criteria[fields.length];
for (int i = 0; i < fields.length; i++) {
criteriaArray[i] = like(fields[i], keyword);
}
// 使用orOperator將多個條件用OR連接
return new Criteria().orOperator(criteriaArray);
}
/**
* 范圍查詢
* 功能:查詢字段值在指定范圍內(nèi)的文檔(包含邊界值)
*
* 實現(xiàn)原理:
* 1. gte: Greater Than or Equal(大于等于)
* 2. lte: Less Than or Equal(小于等于)
* 3. MongoDB的$gte和$lte操作符實現(xiàn)范圍查詢
*
* SQL等價:
* WHERE field >= min AND field <= max
*
* 使用場景:
* - 年齡范圍查詢(18-30歲)
* - 價格范圍查詢(100-500元)
* - 時間范圍查詢(某個時間段的數(shù)據(jù))
*
* 性能優(yōu)化:
* - 為范圍查詢字段添加索引可以顯著提高性能
* - 復(fù)合索引:createIndex({field: 1})
*
* @param fieldName 字段名(要進(jìn)行范圍查詢的字段)
* @param min 最小值(包含)
* @param max 最大值(包含)
* @return 查詢條件對象(Criteria)
*/
public Criteria between(String fieldName, Object min, Object max) {
// gte: 大于等于,lte: 小于等于
return Criteria.where(fieldName).gte(min).lte(max);
}
/**
* 大于查詢
* 功能:查詢字段值大于指定值的文檔
*
* SQL等價:WHERE field > value
*
* @param fieldName 字段名
* @param value 比較值
* @return 查詢條件對象(Criteria)
*/
public Criteria gt(String fieldName, Object value) {
return Criteria.where(fieldName).gt(value);
}
/**
* 小于查詢
* 功能:查詢字段值小于指定值的文檔
*
* SQL等價:WHERE field < value
*
* @param fieldName 字段名
* @param value 比較值
* @return 查詢條件對象(Criteria)
*/
public Criteria lt(String fieldName, Object value) {
return Criteria.where(fieldName).lt(value);
}
/**
* 大于等于查詢
* 功能:查詢字段值大于等于指定值的文檔
*
* SQL等價:WHERE field >= value
*
* @param fieldName 字段名
* @param value 比較值
* @return 查詢條件對象(Criteria)
*/
public Criteria gte(String fieldName, Object value) {
return Criteria.where(fieldName).gte(value);
}
/**
* 小于等于查詢
* 功能:查詢字段值小于等于指定值的文檔
*
* SQL等價:WHERE field <= value
*
* @param fieldName 字段名
* @param value 比較值
* @return 查詢條件對象(Criteria)
*/
public Criteria lte(String fieldName, Object value) {
return Criteria.where(fieldName).lte(value);
}
/**
* 不等于查詢
* 功能:查詢字段值不等于指定值的文檔
*
* SQL等價:WHERE field <> value 或 WHERE field != value
*
* 使用場景:
* - 排除某個值
* - 查詢非空值
*
* @param fieldName 字段名
* @param value 比較值
* @return 查詢條件對象(Criteria)
*/
public Criteria ne(String fieldName, Object value) {
return Criteria.where(fieldName).ne(value);
}
/**
* IN查詢
* 功能:查詢字段值在指定值列表中的文檔
*
* SQL等價:
* WHERE field IN (value1, value2, value3, ...)
*
* 實現(xiàn)原理:
* - MongoDB的$in操作符匹配數(shù)組中的任意一個值
* - 適用于多選條件查詢
*
* 使用場景:
* - 多狀態(tài)查詢(如查詢狀態(tài)為0或1的用戶)
* - ID列表查詢(根據(jù)多個ID查詢對應(yīng)的文檔)
* - 標(biāo)簽查詢(查詢包含某些標(biāo)簽的文檔)
*
* 性能優(yōu)化:
* - 為IN查詢字段添加索引可以顯著提高性能
* - IN列表長度不宜過長(建議不超過100個)
*
* @param fieldName 字段名
* @param values 值列表(Collection類型,如List、Set等)
* @return 查詢條件對象(Criteria)
*/
public Criteria in(String fieldName, Collection<?> values) {
return Criteria.where(fieldName).in(values);
}
/**
* NOT IN查詢
* 功能:查詢字段值不在指定值列表中的文檔
*
* SQL等價:
* WHERE field NOT IN (value1, value2, value3, ...)
*
* 實現(xiàn)原理:
* - MongoDB的$nin操作符匹配數(shù)組外的所有值
* - 適用于排除多個值的場景
*
* 使用場景:
* - 排除多個狀態(tài)
* - 排除某些特定值
*
* 性能注意事項:
* - NOT IN查詢的性能通常低于IN查詢
* - 大列表的NOT IN查詢可能較慢
*
* @param fieldName 字段名
* @param values 值列表(Collection類型)
* @return 查詢條件對象(Criteria)
*/
public Criteria notIn(String fieldName, Collection<?> values) {
return Criteria.where(fieldName).nin(values);
}
/**
* NULL查詢
* 功能:查詢字段值為null的文檔
*
* SQL等價:WHERE field IS NULL
*
* 使用場景:
* - 查找未填寫的字段
* - 數(shù)據(jù)完整性檢查
*
* @param fieldName 字段名
* @return 查詢條件對象(Criteria)
*/
public Criteria isNull(String fieldName) {
return Criteria.where(fieldName).is(null);
}
/**
* NOT NULL查詢
* 功能:查詢字段值不為null的文檔
*
* SQL等價:WHERE field IS NOT NULL
*
* 使用場景:
* - 查找已填寫的字段
* - 過濾掉缺失的數(shù)據(jù)
*
* @param fieldName 字段名
* @return 查詢條件對象(Criteria)
*/
public Criteria notNull(String fieldName) {
return Criteria.where(fieldName).ne(null);
}
/**
* 數(shù)組大小查詢
* 功能:查詢數(shù)組字段長度等于指定值的文檔
*
* 實現(xiàn)原理:
* - MongoDB的$size操作符用于查詢數(shù)組長度
* - 只能匹配精確的數(shù)組長度,不能匹配范圍
*
* 使用場景:
* - 查詢標(biāo)簽數(shù)量(如查詢有3個標(biāo)簽的用戶)
* - 查詢評論數(shù)量(如查詢有10條評論的文章)
*
* 性能注意事項:
* - $size操作符無法使用普通索引
* - 如果數(shù)據(jù)量大,考慮添加專門的索引
*
* @param fieldName 字段名(必須是數(shù)組類型的字段)
* @param size 數(shù)組大?。ň_匹配)
* @return 查詢條件對象(Criteria)
*/
public Criteria size(String fieldName, int size) {
return Criteria.where(fieldName).size(size);
}
/**
* 數(shù)組元素查詢
* 功能:查詢數(shù)組字段中包含指定元素的文檔
*
* 實現(xiàn)原理:
* - 直接使用is()操作符查詢數(shù)組字段
* - 如果數(shù)組中包含該元素,則匹配成功
*
* 使用場景:
* - 查詢包含某個標(biāo)簽的文檔
* - 查詢包含某個值的數(shù)組
*
* 示例:
* - 查詢tags字段包含"java"的文檔:arrayContains("tags", "java")
*
* @param fieldName 字段名(數(shù)組類型字段)
* @param value 數(shù)組元素值(要在數(shù)組中查找的值)
* @return 查詢條件對象(Criteria)
*/
public Criteria arrayContains(String fieldName, Object value) {
return Criteria.where(fieldName).is(value);
}
/**
* 地理位置查詢 - 圓形范圍
* 功能:查詢指定經(jīng)緯度坐標(biāo)附近指定范圍內(nèi)的文檔
*
* 實現(xiàn)原理:
* 1. 創(chuàng)建GeoJsonPoint對象存儲經(jīng)緯度
* 2. 使用$nearSphere操作符進(jìn)行球面距離查詢
* 3. maxDistance參數(shù)需要轉(zhuǎn)換為弧度(除以地球半徑)
*
* 坐標(biāo)說明:
* - 經(jīng)度:東西方向,范圍-180到180
* - 緯度:南北方向,范圍-90到90
* - 中國大致范圍:經(jīng)度73-135,緯度18-53
*
* 距離計算:
* - nearSphere使用球面距離計算(考慮地球曲率)
* - 地球半徑:6378137米(赤道半徑)
* - 轉(zhuǎn)換公式:弧度 = 距離(米) / 地球半徑(米)
*
* 使用場景:
* - 附近的人/商家/酒店查詢
* - 地理位置相關(guān)的推薦
* - 距離排序
*
* 性能優(yōu)化:
* - 必須為地理位置字段創(chuàng)建2dsphere索引
* - 建議同時創(chuàng)建復(fù)合索引提高查詢性能
*
* 示例:
* - 查詢天安門(116.397, 39.918)附近1000米內(nèi)的地點
*
* @param fieldName 字段名(存儲地理坐標(biāo)的字段,類型通常為GeoJsonPoint)
* @param longitude 經(jīng)度(東西方向,-180到180)
* @param latitude 緯度(南北方向,-90到90)
* @param maxDistance 最大距離(單位:米)
* @return 查詢條件對象(Criteria)
*/
public Criteria nearSphere(String fieldName, double longitude, double latitude, double maxDistance) {
// 創(chuàng)建地理坐標(biāo)點對象(經(jīng)度在前,緯度在后)
Point point = new Point(longitude, latitude);
// nearSphere使用球面距離計算,更精確(考慮地球曲率)
// maxDistance需要除以地球半徑(6378137米)轉(zhuǎn)換為弧度
// 因為MongoDB使用弧度作為距離單位
return Criteria.where(fieldName).nearSphere(point).maxDistance(maxDistance / 6378137.0);
}
/**
* 正則表達(dá)式查詢
* 功能:支持使用正則表達(dá)式進(jìn)行靈活匹配
*
* 實現(xiàn)原理:
* - MongoDB的$regex操作符支持PCRE正則表達(dá)式
* - 支持復(fù)雜的字符串匹配模式
*
* 常用正則表達(dá)式示例:
* - ^abc: 以abc開頭
* - abc$: 以abc結(jié)尾
* - a.c: 中間是任意字符
* - a*: 0個或多個a
* - a+: 1個或多個a
* - a?b: 可選的a后面跟著b
*
* 使用場景:
* - 復(fù)雜的字符串匹配
* - 郵箱、手機(jī)號、身份證號驗證
* - 特定格式的數(shù)據(jù)查詢
*
* 性能注意事項:
* - 正則查詢無法使用普通索引(除前綴匹配外)
* - 復(fù)雜的正則表達(dá)式可能導(dǎo)致全表掃描
* - 建議配合其他條件縮小查詢范圍
*
* @param fieldName 字段名
* @param pattern 正則表達(dá)式字符串(PCRE格式)
* @return 查詢條件對象(Criteria)
*/
public Criteria regex(String fieldName, String pattern) {
return Criteria.where(fieldName).regex(pattern);
}
// ==================== 高級更新操作 ====================
/**
* 字段自增
* 功能:將數(shù)值字段的值增加指定數(shù)量(支持正數(shù)和負(fù)數(shù))
*
* 實現(xiàn)原理:
* - MongoDB的$inc操作符用于數(shù)值字段的原子自增/自減
* - 原子操作:不需要先讀再寫,直接在數(shù)據(jù)庫端完成
*
* 使用場景:
* - 計數(shù)器(瀏覽量、點贊數(shù)、評論數(shù)等)
* - 庫存扣減(減法操作)
* - 積分增加(加法操作)
*
* 優(yōu)勢:
* - 原子操作,避免并發(fā)問題
* - 性能優(yōu)于先讀后寫
* - 支持小數(shù)運(yùn)算
*
* 示例:
* - increment("age", 1): 年齡加1
* - increment("view_count", 100): 瀏覽量加100
* - increment("stock", -5): 庫存減5
*
* @param fieldName 字段名(必須是數(shù)值類型)
* @param value 增量值(正數(shù)表示增加,負(fù)數(shù)表示減少)
* @return 更新操作對象(Update)
*/
public Update increment(String fieldName, Number value) {
return new Update().inc(fieldName, value);
}
/**
* 字段自乘
* 功能:將數(shù)值字段的值乘以指定倍數(shù)
*
* 實現(xiàn)原理:
* - MongoDB的$mul操作符用于數(shù)值字段的乘法運(yùn)算
* - 原子操作,避免并發(fā)問題
*
* 使用場景:
* - 價格調(diào)整(打折扣)
* - 權(quán)重計算
* - 比例調(diào)整
*
* 示例:
* - multiply("price", 0.9): 價格打9折
* - multiply("quantity", 2): 數(shù)量翻倍
*
* @param fieldName 字段名(必須是數(shù)值類型)
* @param value 乘數(shù)(乘數(shù))
* @return 更新操作對象(Update)
*/
public Update multiply(String fieldName, Number value) {
return new Update().mul(fieldName, value);
}
/**
* 數(shù)組添加元素
* 功能:向數(shù)組字段添加一個元素(如果數(shù)組不存在則創(chuàng)建)
*
* 實現(xiàn)原理:
* - MongoDB的$push操作符用于向數(shù)組添加元素
* - 如果字段不存在,會自動創(chuàng)建并添加元素
* - 如果字段存在但不是數(shù)組,會報錯
*
* 使用場景:
* - 添加標(biāo)簽
* - 添加評論
* - 添加收藏項
*
* 示例:
* - push("tags", "java"): 向tags數(shù)組添加"java"
*
* 注意事項:
* - 此方法會在數(shù)組末尾添加元素
* - 如果要添加多個元素,使用pushAll()
* - 如果要避免重復(fù)元素,使用$addToSet(Spring Data MongoDB默認(rèn))
*
* @param fieldName 字段名(數(shù)組類型字段)
* @param value 要添加的元素值
* @return 更新操作對象(Update)
*/
public Update push(String fieldName, Object value) {
return new Update().push(fieldName, value);
}
/**
* 數(shù)組批量添加元素
* 功能:向數(shù)組字段一次性添加多個元素
*
* 實現(xiàn)原理:
* - MongoDB的$pushAll操作符用于批量添加數(shù)組元素
* - 一次操作添加多個元素,提高性能
*
* 使用場景:
* - 批量添加標(biāo)簽
* - 批量添加列表項
*
* 性能優(yōu)勢:
* - 相比循環(huán)調(diào)用push(),此方法性能更好
* - 減少網(wǎng)絡(luò)往返次數(shù)
*
* @param fieldName 字段名(數(shù)組類型字段)
* @param values 要添加的元素數(shù)組
* @return 更新操作對象(Update)
*/
public Update pushAll(String fieldName, Object[] values) {
return new Update().pushAll(fieldName, values);
}
/**
* 數(shù)組刪除元素
* 功能:從數(shù)組字段中刪除指定值的所有匹配項
*
* 實現(xiàn)原理:
* - MongoDB的$pull操作符用于刪除數(shù)組中匹配的元素
* - 如果有多個相同的值,都會被刪除
* - 如果值不存在,不會報錯
*
* 使用場景:
* - 刪除標(biāo)簽
* - 刪除評論
* - 刪除列表項
*
* 示例:
* - pull("tags", "java"): 從tags數(shù)組刪除所有"java"
*
* @param fieldName 字段名(數(shù)組類型字段)
* @param value 要刪除的元素值
* @return 更新操作對象(Update)
*/
public Update pull(String fieldName, Object value) {
return new Update().pull(fieldName, value);
}
/**
* 數(shù)組刪除多個元素
* 功能:從數(shù)組字段中批量刪除指定的多個元素
*
* 實現(xiàn)原理:
* - MongoDB的$pullAll操作符用于批量刪除數(shù)組元素
* - 一次操作刪除多個元素,提高性能
*
* 使用場景:
* - 批量刪除標(biāo)簽
* - 批量刪除列表項
*
* @param fieldName 字段名(數(shù)組類型字段)
* @param values 要刪除的元素數(shù)組
* @return 更新操作對象(Update)
*/
public Update pullAll(String fieldName, Object[] values) {
return new Update().pullAll(fieldName, values);
}
/**
* 修改數(shù)組中指定位置的元素
* 功能:使用位置運(yùn)算符更新數(shù)組中特定位置的元素
*
* 實現(xiàn)原理:
* - 使用$set操作符更新指定位置的數(shù)組元素
* - 需要配合查詢條件確定數(shù)組索引
*
* 使用方式:
* 1. 使用字段名加索引的方式指定位置,如"tags.0"表示第一個元素
* 2. 可以配合查詢條件使用,如"tags.$.value"(使用$表示匹配的元素)
*
* 使用場景:
* - 修改數(shù)組中特定位置的元素
* - 配合$elemMatch使用
*
* 示例:
* - setAtIndex("tags.0", "python"): 修改tags數(shù)組的第一個元素為"python"
*
* 注意事項:
* - 直接指定索引時,需要確保索引在數(shù)組范圍內(nèi)
* - 建議配合查詢條件使用更安全
*
* @param fieldName 字段名(包含位置表達(dá)式,如"tags.0"或使用$運(yùn)算符)
* @param value 新的元素值
* @return 更新操作對象(Update)
*/
public Update setAtIndex(String fieldName, Object value) {
return new Update().set(fieldName, value);
}
/**
* 如果字段不存在則設(shè)置
* 功能:僅在插入新文檔時設(shè)置字段值,更新時不會覆蓋已存在的字段
*
* 實現(xiàn)原理:
* - MongoDB的$setOnInsert操作符
* - 只在文檔不存在(即插入新文檔)時設(shè)置字段
* - 如果文檔已存在(即更新操作),此字段不會被修改
*
* 使用場景:
* - 設(shè)置創(chuàng)建時間(只在創(chuàng)建時設(shè)置,更新時不修改)
* - 設(shè)置默認(rèn)值(只在首次創(chuàng)建時設(shè)置)
* - 設(shè)置初始化標(biāo)志
*
* 示例:
* - setOnInsert("create_time", new Date()): 只在插入時設(shè)置創(chuàng)建時間
* - setOnInsert("version", 1): 只在插入時設(shè)置初始版本號
*
* 注意事項:
* - 通常與upsert操作配合使用
* - 如果不使用upsert,此操作無意義
*
* @param fieldName 字段名
* @param value 字段值
* @return 更新操作對象(Update)
*/
public Update setOnInsert(String fieldName, Object value) {
return new Update().setOnInsert(fieldName, value);
}
/**
* 重命名字段
* 功能:將文檔中的字段重命名
*
* 實現(xiàn)原理:
* - MongoDB的$rename操作符
* - 原子操作,不需要先讀后寫
*
* 使用場景:
* - 字段名優(yōu)化(如將old_name改為newName)
* - 數(shù)據(jù)結(jié)構(gòu)遷移
* - 字段名規(guī)范化
*
* 示例:
* - rename("username", "user_name"): 將username字段重命名為user_name
*
* 注意事項:
* - 如果新字段名已存在,會被覆蓋
* - 如果舊字段名不存在,不會有任何效果
* - 此操作在大型集合上可能較慢
*
* @param oldName 舊字段名
* @param newName 新字段名
* @return 更新操作對象(Update)
*/
public Update rename(String oldName, String newName) {
return new Update().rename(oldName, newName);
}
/**
* 刪除字段
* 功能:從文檔中刪除指定字段
*
* 實現(xiàn)原理:
* - MongoDB的$unset操作符
* - 原子操作,不需要先讀后寫
*
* 使用場景:
* - 數(shù)據(jù)清理(刪除不需要的字段)
* - 敏感信息刪除
* - 數(shù)據(jù)結(jié)構(gòu)優(yōu)化
*
* 示例:
* - unset("password"): 刪除password字段
* - unset("temporary_field"): 刪除臨時字段
*
* 注意事項:
* - 如果字段不存在,不會有任何效果(不會報錯)
* - 刪除操作是永久的,無法恢復(fù)
*
* @param fieldName 字段名
* @return 更新操作對象(Update)
*/
public Update unset(String fieldName) {
return new Update().unset(fieldName);
}
/**
* 當(dāng)前時間更新
* 功能:將指定字段更新為當(dāng)前日期時間
*
* 實現(xiàn)原理:
* - MongoDB的$currentDate操作符
* - 使用服務(wù)器當(dāng)前時間
* - 原子操作,不需要先讀后寫
*
* 使用場景:
* - 更新時間戳(最后修改時間)
* - 記錄更新時間
* - 審計日志
*
* 示例:
* - currentDate("update_time"): 將update_time字段更新為當(dāng)前時間
* - currentDate("last_modified"): 將last_modified字段更新為當(dāng)前時間
*
* 優(yōu)勢:
* - 不需要在Java代碼中獲取時間
* - 避免客戶端和服務(wù)器時間不一致的問題
* - 性能優(yōu)于先讀后寫
*
* @param fieldName 字段名(通常是日期或時間戳類型)
* @return 更新操作對象(Update)
*/
public Update currentDate(String fieldName) {
return new Update().currentDate(fieldName);
}
// ==================== 聚合操作 ====================
/**
* 執(zhí)行聚合查詢
* 功能:執(zhí)行自定義的聚合管道,支持復(fù)雜的數(shù)據(jù)處理和統(tǒng)計
*
* 聚合管道概念:
* - 聚合管道是MongoDB強(qiáng)大的數(shù)據(jù)處理功能
* - 通過多個階段(stage)依次處理數(shù)據(jù)
* - 每個階段接收上階段的輸出作為輸入
*
* 常用聚合階段:
* - $match: 過濾文檔(類似WHERE)
* - $group: 分組統(tǒng)計(類似GROUP BY)
* - $sort: 排序(類似ORDER BY)
* - $project: 投影(選擇字段)
* - $limit: 限制返回數(shù)量
* - $skip: 跳過指定數(shù)量
* - $lookup: 關(guān)聯(lián)查詢(類似JOIN)
* - $unwind: 拆分?jǐn)?shù)組
*
* 使用場景:
* - 復(fù)雜的數(shù)據(jù)統(tǒng)計
* - 數(shù)據(jù)報表生成
* - 數(shù)據(jù)分析
* - 數(shù)據(jù)關(guān)聯(lián)查詢
*
* 性能優(yōu)化:
* - 盡早使用$match過濾數(shù)據(jù)
* - 為查詢字段添加索引
* - 合理使用投影減少數(shù)據(jù)傳輸量
*
* @param aggregation 聚合操作對象(包含多個聚合階段)
* @param entityClass 實體類類型(輸入類型)
* @return 聚合結(jié)果對象(包含映射結(jié)果)
*/
public AggregationResults<T> aggregate(Aggregation aggregation, Class<T> entityClass) {
return mongoTemplate.aggregate(aggregation, entityClass, entityClass);
}
/**
* 分組計數(shù)
* 功能:按指定字段分組,統(tǒng)計每組的文檔數(shù)量
*
* SQL等價:
* SELECT field, COUNT(*) as count FROM table GROUP BY field ORDER BY count DESC
*
* 聚合管道:
* 1. $group: 按指定字段分組,使用count()統(tǒng)計數(shù)量
* 2. $sort: 按計數(shù)降序排序
*
* 使用場景:
* - 統(tǒng)計各分類下的文章數(shù)量
* - 統(tǒng)計各狀態(tài)的用戶數(shù)量
* - 統(tǒng)計各個地區(qū)的訂單數(shù)量
*
* 返回結(jié)果:
* - List<Map>,每個Map包含:
* - _id: 分組字段的值
* - count: 文檔數(shù)量
*
* @param groupByField 分組字段名(要按哪個字段分組)
* @param entityClass 實體類類型
* @return 分組統(tǒng)計結(jié)果列表(每項包含字段值和計數(shù))
*/
public List<Map> groupCount(String groupByField, Class<T> entityClass) {
// 構(gòu)建聚合管道:
// 1. $group: 按指定字段分組,并統(tǒng)計每組的文檔數(shù)
// 2. $sort: 按計數(shù)降序排序
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group(groupByField).count().as("count"),
Aggregation.sort(Sort.Direction.DESC, "count")
);
AggregationResults<Map> results = mongoTemplate.aggregate(
aggregation, entityClass, Map.class);
return results.getMappedResults();
}
/**
* 分組求和
* 功能:按指定字段分組,計算每組的數(shù)值字段總和
*
* SQL等價:
* SELECT field, SUM(num_field) as total FROM table GROUP BY field ORDER BY total DESC
*
* 聚合管道:
* 1. $group: 按指定字段分組,使用sum()求和
* 2. $sort: 按總和降序排序
*
* 使用場景:
* - 統(tǒng)計各分類的總銷售額
* - 統(tǒng)計各部門的總支出
* - 統(tǒng)計各用戶的總消費
*
* 返回結(jié)果:
* - List<Map>,每個Map包含:
* - _id: 分組字段的值
* - total: 總和
*
* @param groupByField 分組字段名
* @param sumField 要求和的數(shù)值字段名
* @param entityClass 實體類類型
* @return 分組求和結(jié)果列表
*/
public List<Map> groupSum(String groupByField, String sumField, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group(groupByField).sum(sumField).as("total"),
Aggregation.sort(Sort.Direction.DESC, "total")
);
AggregationResults<Map> results = mongoTemplate.aggregate(
aggregation, entityClass, Map.class);
return results.getMappedResults();
}
/**
* 分組求平均值
* 功能:按指定字段分組,計算每組的數(shù)值字段平均值
*
* SQL等價:
* SELECT field, AVG(num_field) as average FROM table GROUP BY field ORDER BY average DESC
*
* 聚合管道:
* 1. $group: 按指定字段分組,使用avg()求平均
* 2. $sort: 按平均值降序排序
*
* 使用場景:
* - 統(tǒng)計各分類的平均價格
* - 統(tǒng)計各班級的平均分
* - 統(tǒng)計各員工的平均績效
*
* 返回結(jié)果:
* - List<Map>,每個Map包含:
* - _id: 分組字段的值
* - average: 平均值
*
* @param groupByField 分組字段名
* @param avgField 要求平均的數(shù)值字段名
* @param entityClass 實體類類型
* @return 分組求平均結(jié)果列表
*/
public List<Map> groupAvg(String groupByField, String avgField, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group(groupByField).avg(avgField).as("average"),
Aggregation.sort(Sort.Direction.DESC, "average")
);
AggregationResults<Map> results = mongoTemplate.aggregate(
aggregation, entityClass, Map.class);
return results.getMappedResults();
}
/**
* 聚合分頁查詢
* 功能:對聚合查詢結(jié)果進(jìn)行分頁,支持統(tǒng)計總數(shù)和返回分頁數(shù)據(jù)
*
* 實現(xiàn)難點:
* - 聚合查詢的分頁需要執(zhí)行兩次聚合
* - 第一次:統(tǒng)計總數(shù)(使用$count)
* - 第二次:分頁查詢(使用$skip和$limit)
*
* 執(zhí)行邏輯:
* 1. 如果指定了排序,添加排序階段
* 2. 第一次聚合:統(tǒng)計總數(shù)
* 3. 第二次聚合:執(zhí)行分頁查詢
* 4. 構(gòu)造Page對象返回
*
* 性能優(yōu)化:
* - 兩次聚合可能影響性能
* - 考慮使用緩存優(yōu)化
* - 對于大數(shù)據(jù)集,考慮使用游標(biāo)分頁
*
* @param aggregation 聚合操作對象
* @param pageRequest 分頁參數(shù)對象
* @param entityClass 實體類類型
* @return 分頁結(jié)果對象(Page接口)
*/
public Page<T> aggregatePage(Aggregation aggregation, PageRequest pageRequest,
Class<T> entityClass) {
// 如果指定了排序字段,添加排序條件
if (pageRequest.getSortField() != null) {
Sort.Direction direction = "desc".equalsIgnoreCase(pageRequest.getSortOrder())
? Sort.Direction.DESC : Sort.Direction.ASC;
// 在原有聚合操作基礎(chǔ)上追加排序階段
aggregation = Aggregation.newAggregation(
aggregation.getOperations(),
Aggregation.sort(Sort.by(direction, pageRequest.getSortField()))
);
}
// 第一次聚合:統(tǒng)計總數(shù)
// 在聚合管道末尾添加$count階段
Aggregation countAggregation = Aggregation.newAggregation(
aggregation.getOperations(),
Aggregation.count().as("total")
);
AggregationResults<Map> countResults = mongoTemplate.aggregate(
countAggregation, entityClass, Map.class);
// 提取總數(shù),如果沒有結(jié)果則為0
long total = countResults.getMappedResults().isEmpty() ? 0 :
(Long) countResults.getMappedResults().get(0).get("total");
// 第二次聚合:執(zhí)行分頁查詢
// 在聚合管道末尾添加$skip和$limit階段
Aggregation pageAggregation = Aggregation.newAggregation(
aggregation.getOperations(),
Aggregation.skip(pageRequest.getSkip()),
Aggregation.limit(pageRequest.getPageSize())
);
AggregationResults<T> results = mongoTemplate.aggregate(
pageAggregation, entityClass, entityClass);
// 構(gòu)造并返回Page對象
return new PageImpl<>(results.getMappedResults(),
PageRequest.of(pageRequest.getPageNum() - 1, pageRequest.getPageSize()),
total);
}
/**
* 查找最大值
* 功能:查詢指定字段的最大值
*
* SQL等價:SELECT MAX(field) as maxValue FROM table
*
* 聚合管道:
* - $group: 不分組(使用空的group()),使用max()求最大值
*
* 使用場景:
* - 查找最高價格
* - 查找最大年齡
* - 查找最新時間
*
* @param fieldName 字段名(必須是數(shù)值或日期類型)
* @param entityClass 實體類類型
* @return 最大值對象(如果沒有數(shù)據(jù)則返回null)
*/
public Object max(String fieldName, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group().max(fieldName).as("maxValue")
);
AggregationResults<Map> results = mongoTemplate.aggregate(
aggregation, entityClass, Map.class);
return results.getMappedResults().isEmpty() ? null :
results.getMappedResults().get(0).get("maxValue");
}
/**
* 查找最小值
* 功能:查詢指定字段的最小值
*
* SQL等價:SELECT MIN(field) as minValue FROM table
*
* 聚合管道:
* - $group: 不分組(使用空的group()),使用min()求最小值
*
* 使用場景:
* - 查找最低價格
* - 查找最小年齡
* - 查找最早時間
*
* @param fieldName 字段名(必須是數(shù)值或日期類型)
* @param entityClass 實體類類型
* @return 最小值對象(如果沒有數(shù)據(jù)則返回null)
*/
public Object min(String fieldName, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group().min(fieldName).as("minValue")
);
AggregationResults<Map> results = mongoTemplate.aggregate(
aggregation, entityClass, Map.class);
return results.getMappedResults().isEmpty() ? null :
results.getMappedResults().get(0).get("minValue");
}
/**
* 查找平均值
* 功能:查詢指定字段的平均值
*
* SQL等價:SELECT AVG(field) as avgValue FROM table
*
* 聚合管道:
* - $group: 不分組(使用空的group()),使用avg()求平均值
*
* 使用場景:
* - 計算平均價格
* - 計算平均年齡
* - 計算平均分
*
* @param fieldName 字段名(必須是數(shù)值類型)
* @param entityClass 實體類類型
* @return 平均值(Double類型,如果沒有數(shù)據(jù)則返回null)
*/
public Double avg(String fieldName, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group().avg(fieldName).as("avgValue")
);
AggregationResults<Map> results = mongoTemplate.aggregate(
aggregation, entityClass, Map.class);
return results.getMappedResults().isEmpty() ? null :
(Double) results.getMappedResults().get(0).get("avgValue");
}
/**
* 求和
* 功能:查詢指定字段的總和
*
* SQL等價:SELECT SUM(field) as sumValue FROM table
*
* 聚合管道:
* - $group: 不分組(使用空的group()),使用sum()求和
*
* 使用場景:
* - 計算總銷售額
* - 計算總數(shù)量
* - 計算總積分
*
* @param fieldName 字段名(必須是數(shù)值類型)
* @param entityClass 實體類類型
* @return 總和(Double類型,如果沒有數(shù)據(jù)則返回null)
*/
public Double sum(String fieldName, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group().sum(fieldName).as("sumValue")
);
AggregationResults<Map> results = mongoTemplate.aggregate(
aggregation, entityClass, Map.class);
return results.getMappedResults().isEmpty() ? null :
((Number) results.getMappedResults().get(0).get("sumValue")).doubleValue();
}
// ==================== 事務(wù)處理 ====================
/**
* 執(zhí)行事務(wù)操作(無返回值)
* 功能:在事務(wù)中執(zhí)行操作,發(fā)生異常時自動回滾
*
* MongoDB事務(wù)特性:
* - 需要MongoDB 4.0+版本
* - 需要副本集環(huán)境(Replica Set)
* - 支持ACID特性(原子性、一致性、隔離性、持久性)
* - 同一事務(wù)內(nèi)只能操作同一個數(shù)據(jù)庫
*
* 使用場景:
* - 轉(zhuǎn)賬操作(涉及兩個賬戶)
* - 訂單處理(訂單、庫存、日志同時更新)
* - 多步操作需要保證數(shù)據(jù)一致性
*
* 性能注意事項:
* - 事務(wù)操作會降低性能
* - 事務(wù)執(zhí)行時間不宜過長(建議不超過60秒)
* - 避免在事務(wù)中執(zhí)行大量查詢
*
* 示例:
* ```java
* mongoUtils.executeInTransaction(() -> {
* // 扣減余額
* updateBalance(fromId, -100);
* // 增加余額
* updateBalance(toId, 100);
* });
* ```
*
* @param action 要執(zhí)行的操作(Runnable接口,無返回值)
* @return 操作是否成功(成功返回true,拋出異常返回false)
*/
@Transactional(rollbackFor = Exception.class)
public boolean executeInTransaction(Runnable action) {
try {
// 執(zhí)行事務(wù)內(nèi)的操作
action.run();
// 如果沒有異常,返回true表示成功
return true;
} catch (Exception e) {
// 記錄錯誤日志
log.error("事務(wù)執(zhí)行失敗", e);
// 重新拋出異常以觸發(fā)事務(wù)回滾
throw e;
}
}
/**
* 執(zhí)行事務(wù)操作并返回結(jié)果
* 功能:在事務(wù)中執(zhí)行有返回值的操作,發(fā)生異常時自動回滾
*
* 與executeInTransaction()的區(qū)別:
* - 此方法可以返回操作結(jié)果
* - 使用Supplier接口(有返回值)
* - 適用于需要獲取操作結(jié)果的場景
*
* 使用場景:
* - 創(chuàng)建對象并返回ID
* - 執(zhí)行計算并返回結(jié)果
* - 任何需要返回值的事務(wù)操作
*
* 示例:
* ```java
* User user = mongoUtils.executeInTransactionWithResult(() -> {
* // 創(chuàng)建用戶
* return saveUser(user);
* // 創(chuàng)建關(guān)聯(lián)數(shù)據(jù)
* saveProfile(profile);
* });
* ```
*
* @param action 要執(zhí)行的操作(Supplier接口,有返回值)
* @param <R> 返回值類型
* @return 操作結(jié)果
* @throws RuntimeException 如果事務(wù)執(zhí)行失敗,拋出異常觸發(fā)回滾
*/
@Transactional(rollbackFor = Exception.class)
public <R> R executeInTransactionWithResult(java.util.function.Supplier<R> action) {
try {
// 執(zhí)行事務(wù)內(nèi)的操作并返回結(jié)果
return action.get();
} catch (Exception e) {
// 記錄錯誤日志
log.error("事務(wù)執(zhí)行失敗", e);
// 重新拋出異常以觸發(fā)事務(wù)回滾
throw e;
}
}
// ==================== 索引操作 ====================
/**
* 創(chuàng)建索引
* 功能:根據(jù)實體類上的@Indexed注解創(chuàng)建索引
*
* 索引類型:
* - 單字段索引:createIndex({field: 1})
* - 復(fù)合索引:createIndex({field1: 1, field2: -1})
* - 唯一索引:@Indexed(unique = true)
* - 過期索引:@Indexed(expireAfterSeconds = 3600)
* - 地理索引:@GeoSpatialIndexed
*
* 性能影響:
* - 索引會提高查詢性能
* - 索引會降低插入、更新、刪除性能
* - 索引占用磁盤空間
*
* 最佳實踐:
* - 為常用查詢字段創(chuàng)建索引
* - 避免過度索引
* - 定期分析慢查詢,優(yōu)化索引
*
* @param entityClass 實體類類型(包含@Indexed注解)
*/
public void createIndex(Class<T> entityClass) {
mongoTemplate.indexOps(entityClass).ensureIndexes();
}
/**
* 刪除索引
* 功能:刪除集合的所有索引(保留_id索引)
*
* 注意事項:
* - _id索引無法被刪除(MongoDB默認(rèn)索引)
* - 刪除索引后,相關(guān)查詢性能會下降
* - 生產(chǎn)環(huán)境中謹(jǐn)慎使用
*
* 使用場景:
* - 重建索引(刪除后重新創(chuàng)建)
* - 數(shù)據(jù)遷移
* - 性能測試
*
* @param entityClass 實體類類型
*/
public void dropIndex(Class<T> entityClass) {
mongoTemplate.indexOps(entityClass).dropAllIndexes();
}
/**
* 獲取所有索引
* 功能:查詢集合的所有索引信息
*
* 返回信息:
* - 索引名稱
* - 索引字段
* - 索引類型(單字段、復(fù)合、唯一等)
* - 索引大小
*
* 使用場景:
* - 檢查索引創(chuàng)建情況
* - 性能分析
* - 索引優(yōu)化
*
* @param entityClass 實體類類型
* @return 索引信息列表(IndexInfo對象列表)
*/
public List<IndexInfo> getIndexes(Class<T> entityClass) {
return mongoTemplate.indexOps(entityClass).getIndexInfo();
}
// ==================== 批量操作 ====================
/**
* 批量插入
* 功能:批量插入多個文檔
*
* 性能優(yōu)勢:
* - 減少網(wǎng)絡(luò)往返次數(shù)
* - MongoDB內(nèi)部優(yōu)化批量寫入
*
* 使用場景:
* - 數(shù)據(jù)初始化
* - 數(shù)據(jù)導(dǎo)入
* - 批量新增
*
* @param entities 實體集合
* @param entityClass 實體類類型
* @return 插入的實體集合
*/
public List<T> bulkInsert(Collection<T> entities, Class<T> entityClass) {
return (List<T>) mongoTemplate.insertAll(entities);
}
/**
* 批量更新
* 功能:執(zhí)行多個更新操作,一次性提交,提高性能
*
* 實現(xiàn)原理:
* - 使用BulkOperations進(jìn)行批量操作
* - UNORDERED模式:無序執(zhí)行,效率更高
* - 一次性提交所有更新操作
*
* 性能優(yōu)勢:
* - 減少網(wǎng)絡(luò)往返次數(shù)
* - MongoDB內(nèi)部優(yōu)化批量執(zhí)行
* - 顯著提高大量更新的性能
*
* 使用場景:
* - 批量修改數(shù)據(jù)
* - 數(shù)據(jù)修復(fù)
* - 批量更新狀態(tài)
*
* 示例:
* ```java
* List<BulkUpdateOperation> updates = new ArrayList<>();
* updates.add(new BulkUpdateOperation(query1, update1));
* updates.add(new BulkUpdateOperation(query2, update2));
* BulkOperationsResult result = bulkUpdate(updates, User.class);
* ```
*
* @param updates 更新操作列表(每個元素包含查詢條件和更新內(nèi)容)
* @param entityClass 實體類類型
* @return 批量操作結(jié)果(包含插入、修改、刪除的文檔數(shù))
*/
public BulkOperationsResult bulkUpdate(List<BulkUpdateOperation> updates, Class<T> entityClass) {
// 創(chuàng)建批量操作對象,使用UNORDERED模式(無序執(zhí)行,效率更高)
// UNORDERED: 并發(fā)執(zhí)行,速度快但錯誤處理復(fù)雜
// ORDERED: 順序執(zhí)行,遇到錯誤停止,速度較慢
BulkOperations bulkOps = mongoTemplate.bulkOps(
BulkOperations.BulkMode.UNORDERED, entityClass);
// 將每個更新操作添加到批量操作中
for (BulkUpdateOperation update : updates) {
bulkOps.updateOne(update.getQuery(), update.getUpdate());
}
// 執(zhí)行批量操作并獲取結(jié)果
com.mongodb.client.result.BulkWriteResult result = bulkOps.execute();
// 封裝并返回批量操作結(jié)果
return new BulkOperationsResult(
result.getInsertedCount(),
result.getModifiedCount(),
result.getDeletedCount()
);
}
/**
* 批量操作結(jié)果封裝類
* 功能:封裝批量操作的執(zhí)行結(jié)果,方便調(diào)用方獲取操作統(tǒng)計信息
*
* 包含信息:
* - insertedCount: 插入的文檔數(shù)
* - modifiedCount: 修改的文檔數(shù)
* - deletedCount: 刪除的文檔數(shù)
*
* 使用場景:
* - 統(tǒng)計批量操作的影響范圍
* - 日志記錄
* - 操作驗證
*/
@Data
public static class BulkOperationsResult {
// 插入的文檔數(shù)
private long insertedCount;
// 修改的文檔數(shù)
private long modifiedCount;
// 刪除的文檔數(shù)
private long deletedCount;
/**
* 構(gòu)造函數(shù)
* @param insertedCount 插入的文檔數(shù)
* @param modifiedCount 修改的文檔數(shù)
* @param deletedCount 刪除的文檔數(shù)
*/
public BulkOperationsResult(long insertedCount, long modifiedCount, long deletedCount) {
this.insertedCount = insertedCount;
this.modifiedCount = modifiedCount;
this.deletedCount = deletedCount;
}
}
/**
* 批量更新操作封裝類
* 功能:封裝單個批量更新操作的查詢條件和更新內(nèi)容
*
* 包含信息:
* - query: 查詢條件(用于匹配要更新的文檔)
* - update: 更新操作(包含要更新的字段和新值)
*
* 使用場景:
* - 構(gòu)造批量更新操作列表
* - 傳遞給bulkUpdate()方法
*
* 示例:
* ```java
* Query query = new Query(Criteria.where("status").is(0));
* Update update = new Update().set("status", 1);
* BulkUpdateOperation operation = new BulkUpdateOperation(query, update);
* ```
*/
@Data
public static class BulkUpdateOperation {
// 查詢條件(用于匹配要更新的文檔)
private Query query;
// 更新操作(包含要更新的字段和新值)
private Update update;
/**
* 構(gòu)造函數(shù)
* @param query 查詢條件
* @param update 更新操作
*/
public BulkUpdateOperation(Query query, Update update) {
this.query = query;
this.update = update;
}
}
}
基礎(chǔ)CRUD操作
實體類示例
package com.example.mongodb.entity;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 用戶實體類
*
* 注解說明:
* - @Document: 指定對應(yīng)的MongoDB集合名稱
* - @Id: 指定主鍵字段
* - @Indexed: 為字段創(chuàng)建索引
* - @Field: 指定字段在MongoDB中的名稱
*/
@Data
@Document(collection = "user")
public class User implements Serializable {
// 主鍵,MongoDB自動生成ObjectId
@Id
private String id;
// 用戶名,創(chuàng)建唯一索引
@Indexed(unique = true)
@Field("username")
private String username;
// 密碼
@Field("password")
private String password;
// 郵箱
@Field("email")
private String email;
// 手機(jī)號
@Field("phone")
private String phone;
// 年齡
@Field("age")
private Integer age;
// 性別(0-未知,1-男,2-女)
@Field("gender")
private Integer gender;
// 狀態(tài)(0-禁用,1-啟用)
@Field("status")
private Integer status;
// 標(biāo)簽數(shù)組
@Field("tags")
private List<String> tags;
// 創(chuàng)建時間
@Field("create_time")
private Date createTime;
// 更新時間
@Field("update_time")
private Date updateTime;
}
Service層示例
package com.example.mongodb.service;
import com.example.mongodb.entity.User;
import com.example.mongodb.utils.MongoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
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 java.util.Date;
import java.util.List;
/**
* 用戶服務(wù)類 - 演示如何使用MongoUtils工具類
*/
@Service
public class UserService {
@Autowired
private MongoUtils<User> mongoUtils;
/**
* 保存用戶
* 功能:創(chuàng)建新用戶,設(shè)置創(chuàng)建時間和更新時間
*/
public User saveUser(User user) {
// 設(shè)置創(chuàng)建時間和更新時間
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
// 調(diào)用工具類保存
return mongoUtils.save(user);
}
/**
* 批量保存用戶
* 功能:一次性保存多個用戶
*/
public List<User> saveUsers(List<User> users) {
// 為每個用戶設(shè)置時間
Date now = new Date();
users.forEach(user -> {
user.setCreateTime(now);
user.setUpdateTime(now);
});
// 批量保存
return mongoUtils.saveBatch(users);
}
/**
* 根據(jù)ID查詢用戶
*/
public User getUserById(String id) {
return mongoUtils.findById(id, User.class);
}
/**
* 查詢所有用戶
* 注意:如果數(shù)據(jù)量大,建議使用分頁查詢
*/
public List<User> getAllUsers() {
return mongoUtils.findAll(User.class);
}
/**
* 根據(jù)用戶名查詢用戶
* 功能:通過用戶名精確查詢
*/
public User getUserByUsername(String username) {
Query query = new Query(Criteria.where("username").is(username));
return mongoUtils.findOne(query, User.class);
}
/**
* 根據(jù)用戶名和密碼查詢用戶
* 功能:用戶登錄驗證
*/
public User getUserByUsernameAndPassword(String username, String password) {
Query query = new Query(Criteria.where("username").is(username)
.and("password").is(password));
return mongoUtils.findOne(query, User.class);
}
/**
* 根據(jù)ID更新用戶
* 功能:只更新傳入的字段,其他字段保持不變
*/
public long updateUser(String id, User user) {
// 創(chuàng)建更新對象
Update update = new Update();
// 只更新非空字段
if (user.getUsername() != null) {
update.set("username", user.getUsername());
}
if (user.getPassword() != null) {
update.set("password", user.getPassword());
}
if (user.getEmail() != null) {
update.set("email", user.getEmail());
}
if (user.getPhone() != null) {
update.set("phone", user.getPhone());
}
if (user.getAge() != null) {
update.set("age", user.getAge());
}
if (user.getGender() != null) {
update.set("gender", user.getGender());
}
if (user.getStatus() != null) {
update.set("status", user.getStatus());
}
// 更新時間
update.set("update_time", new Date());
// 執(zhí)行更新并返回修改的文檔數(shù)
return mongoUtils.updateById(id, update, User.class).getModifiedCount();
}
/**
* 根據(jù)ID刪除用戶
*/
public long deleteUser(String id) {
return mongoUtils.deleteById(id, User.class).getDeletedCount();
}
/**
* 檢查用戶名是否存在
*/
public boolean existsUsername(String username) {
Query query = new Query(Criteria.where("username").is(username));
return mongoUtils.exists(query, User.class);
}
/**
* 統(tǒng)計用戶數(shù)量
*/
public long countUsers() {
return mongoUtils.count(new Query(), User.class);
}
}
高級查詢功能
復(fù)雜查詢示例
package com.example.mongodb.service;
import com.example.mongodb.common.PageRequest;
import com.example.mongodb.entity.User;
import com.example.mongodb.utils.MongoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 高級查詢服務(wù)類 - 演示各種高級查詢功能
*/
@Service
public class UserAdvancedQueryService {
@Autowired
private MongoUtils<User> mongoUtils;
/**
* 模糊查詢用戶
* 功能:根據(jù)用戶名模糊查詢,不區(qū)分大小寫
*/
public List<User> searchUsers(String keyword) {
Query query = new Query(mongoUtils.like("username", keyword));
return mongoUtils.find(query, User.class);
}
/**
* 多字段模糊查詢
* 功能:同時在用戶名、郵箱、手機(jī)號中搜索關(guān)鍵詞
*/
public List<User> multiFieldSearch(String keyword) {
String[] fields = {"username", "email", "phone"};
Query query = new Query(mongoUtils.multiFieldLike(fields, keyword));
return mongoUtils.find(query, User.class);
}
/**
* 年齡范圍查詢
* 功能:查詢年齡在指定范圍內(nèi)的用戶
*/
public List<User> getUsersByAgeRange(Integer minAge, Integer maxAge) {
Query query = new Query(mongoUtils.between("age", minAge, maxAge));
return mongoUtils.find(query, User.class);
}
/**
* 狀態(tài)和年齡組合查詢
* 功能:查詢指定狀態(tài)且年齡大于某個值的用戶
*/
public List<User> getUsersByStatusAndAge(Integer status, Integer age) {
Query query = new Query(
Criteria.where("status").is(status)
.and("age").gt(age)
);
return mongoUtils.find(query, User.class);
}
/**
* IN查詢
* 功能:查詢狀態(tài)在指定列表中的用戶
*/
public List<User> getUsersByStatusList(List<Integer> statusList) {
Query query = new Query(mongoUtils.in("status", statusList));
return mongoUtils.find(query, User.class);
}
/**
* 標(biāo)簽包含查詢
* 功能:查詢包含指定標(biāo)簽的用戶
*/
public List<User> getUsersByTag(String tag) {
Query query = new Query(mongoUtils.arrayContains("tags", tag));
return mongoUtils.find(query, User.class);
}
/**
* 復(fù)合條件查詢
* 功能:使用AND連接多個條件
*/
public List<User> getUsersByComplexCondition(String keyword, Integer minAge, List<Integer> statusList) {
Query query = new Query(
new Criteria().andOperator(
mongoUtils.like("username", keyword),
mongoUtils.gte("age", minAge),
mongoUtils.in("status", statusList)
)
);
return mongoUtils.find(query, User.class);
}
/**
* 分頁查詢
* 功能:查詢所有用戶并分頁
*/
public Page<User> getUsersByPage(PageRequest pageRequest) {
Query query = new Query();
return mongoUtils.findPage(query, pageRequest, User.class);
}
/**
* 條件分頁查詢
* 功能:查詢指定狀態(tài)的用戶并分頁
*/
public Page<User> getUsersByPageWithCondition(Integer status, PageRequest pageRequest) {
Query query = new Query(Criteria.where("status").is(status));
return mongoUtils.findPage(query, pageRequest, User.class);
}
}
高級更新操作示例
package com.example.mongodb.service;
import com.example.mongodb.entity.User;
import com.example.mongodb.utils.MongoUtils;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.List;
/**
* 高級更新服務(wù)類 - 演示各種高級更新功能
*/
@Service
public class UserAdvancedUpdateService {
@Autowired
private MongoUtils<User> mongoUtils;
/**
* 用戶年齡自增
* 功能:將指定用戶的年齡加1
*/
public long incrementAge(String userId) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = mongoUtils.increment("age", 1);
return mongoUtils.updateFirst(query, update, User.class).getModifiedCount();
}
/**
* 批量添加標(biāo)簽
* 功能:向用戶的標(biāo)簽數(shù)組添加多個標(biāo)簽
*/
public long addTags(String userId, List<String> newTags) {
Query query = new Query(Criteria.where("_id").is(userId));
for (String tag : newTags) {
Update update = mongoUtils.push("tags", tag);
mongoUtils.updateFirst(query, update, User.class);
}
return newTags.size();
}
/**
* 批量刪除標(biāo)簽
* 功能:從用戶的標(biāo)簽數(shù)組刪除多個標(biāo)簽
*/
public long removeTags(String userId, List<String> tagsToRemove) {
Query query = new Query(Criteria.where("_id").is(userId));
for (String tag : tagsToRemove) {
Update update = mongoUtils.pull("tags", tag);
mongoUtils.updateFirst(query, update, User.class);
}
return tagsToRemove.size();
}
/**
* 批量添加標(biāo)簽(一次性)
* 功能:使用pushAll一次性添加多個標(biāo)簽,性能更好
*/
public long addTagsBatch(String userId, String[] newTags) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = mongoUtils.pushAll("tags", newTags);
return mongoUtils.updateFirst(query, update, User.class).getModifiedCount();
}
/**
* 更新用戶狀態(tài)(存在則更新,不存在則設(shè)置默認(rèn)值)
* 功能:演示setOnInsert的使用
*/
public long updateStatusWithDefault(String userId, Integer status) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = mongoUtils.setOnInsert("status", 1);
update.set("status", status);
return mongoUtils.updateFirst(query, update, User.class).getModifiedCount();
}
/**
* 重命名字段
* 功能:將字段名從oldName改為newName
*/
public long renameField(String userId, String oldName, String newName) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = mongoUtils.rename(oldName, newName);
return mongoUtils.updateFirst(query, update, User.class).getModifiedCount();
}
/**
* 刪除字段
* 功能:從文檔中刪除指定字段
*/
public long removeField(String userId, String fieldName) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = mongoUtils.unset(fieldName);
return mongoUtils.updateFirst(query, update, User.class).getModifiedCount();
}
/**
* 更新時間戳
* 功能:將update_time字段更新為當(dāng)前時間
*/
public long updateTimestamp(String userId) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = mongoUtils.currentDate("update_time");
return mongoUtils.updateFirst(query, update, User.class).getModifiedCount();
}
/**
* 批量更新操作
* 功能:批量更新多個用戶的狀態(tài)
*/
public long bulkUpdateUsers(List<String> userIds, Integer newStatus) {
List<MongoUtils.BulkUpdateOperation> updates = new java.util.ArrayList<>();
for (String userId : userIds) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = new Update().set("status", newStatus);
updates.add(new MongoUtils.BulkUpdateOperation(query, update));
}
MongoUtils.BulkOperationsResult result = mongoUtils.bulkUpdate(updates, User.class);
return result.getModifiedCount();
}
}
聚合操作示例
package com.example.mongodb.service;
import com.example.mongodb.entity.User;
import com.example.mongodb.utils.MongoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* 聚合查詢服務(wù)類 - 演示各種聚合操作
*/
@Service
public class UserAggregationService {
@Autowired
private MongoUtils<User> mongoUtils;
/**
* 按性別分組統(tǒng)計用戶數(shù)量
*/
public List<Map> groupCountByGender() {
return mongoUtils.groupCount("gender", User.class);
}
/**
* 按狀態(tài)分組統(tǒng)計用戶數(shù)量
*/
public List<Map> groupCountByStatus() {
return mongoUtils.groupCount("status", User.class);
}
/**
* 按年齡段分組統(tǒng)計
* 功能:自定義聚合管道,按10歲一個年齡段分組
*/
public List<Map> groupCountByAgeRange() {
Aggregation aggregation = Aggregation.newAggregation(
// 使用表達(dá)式將年齡分段:0-9, 10-19, 20-29...
Aggregation.project()
.andExpression("floor(age / 10) * 10").as("ageRange"),
// 按年齡段分組統(tǒng)計
Aggregation.group("ageRange")
.count().as("count"),
// 按年齡段升序排序
Aggregation.sort(org.springframework.data.domain.Sort.Direction.ASC, "_id")
);
AggregationResults<Map> results = mongoUtils.aggregate(aggregation, User.class, Map.class);
return results.getMappedResults();
}
/**
* 統(tǒng)計各狀態(tài)用戶的平均年齡
*/
public List<Map> groupAvgAgeByStatus() {
return mongoUtils.groupAvg("status", "age", User.class);
}
/**
* 查找最大年齡
*/
public Object getMaxAge() {
return mongoUtils.max("age", User.class);
}
/**
* 查找最小年齡
*/
public Object getMinAge() {
return mongoUtils.min("age", User.class);
}
/**
* 計算平均年齡
*/
public Double getAvgAge() {
return mongoUtils.avg("age", User.class);
}
/**
* 統(tǒng)計總年齡
*/
public Double getSumAge() {
return mongoUtils.sum("age", User.class);
}
/**
* 復(fù)雜聚合查詢:按狀態(tài)分組,統(tǒng)計數(shù)量、平均年齡、最大年齡
*/
public List<Map> complexGroupByStatus() {
Aggregation aggregation = Aggregation.newAggregation(
// 按狀態(tài)分組
Aggregation.group("status")
// 統(tǒng)計用戶數(shù)
.count().as("userCount")
// 統(tǒng)計平均年齡
.avg("age").as("avgAge")
// 統(tǒng)計最大年齡
.max("age").as("maxAge")
// 統(tǒng)計最小年齡
.min("age").as("minAge"),
// 按用戶數(shù)降序排序
Aggregation.sort(org.springframework.data.domain.Sort.Direction.DESC, "userCount")
);
AggregationResults<Map> results = mongoUtils.aggregate(aggregation, User.class, Map.class);
return results.getMappedResults();
}
}
最佳實踐
1. 索引優(yōu)化
為常用查詢字段創(chuàng)建索引
@Indexed private String username;
創(chuàng)建復(fù)合索引優(yōu)化多字段查詢
@CompoundIndex(name = "idx_status_create_time", def = "{'status': 1, 'createTime': -1}")
避免過度索引
- 索引會提高查詢性能,但會降低寫入性能
- 建議為高頻查詢字段創(chuàng)建索引
- 定期分析慢查詢,優(yōu)化索引
2. 查詢優(yōu)化
只查詢需要的字段
Query query = new Query();
query.fields().include("username").include("email");
避免深度分頁
- skip()在數(shù)據(jù)量大時性能較差
- 考慮使用基于游標(biāo)的分頁
使用索引覆蓋查詢
// 查詢條件和排序都使用索引字段
query.addCriteria(Criteria.where("status").is(1));
query.with(Sort.by(Sort.Direction.DESC, "createTime"));
3. 批量操作
大量數(shù)據(jù)插入使用批量插入
mongoUtils.saveBatch(users);
多文檔更新使用BulkOperations
mongoUtils.bulkUpdate(updates, User.class);
總結(jié)
本文詳細(xì)介紹了SpringBoot整合MongoDB的完整工具類實現(xiàn),涵蓋了基礎(chǔ)CRUD、高級查詢、聚合操作、事務(wù)處理等功能。通過合理封裝MongoDB操作,可以顯著提高開發(fā)效率和代碼質(zhì)量。
以上就是SpringBoot整合MongoDB的完整操作指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot整合MongoDB的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot log4j2不能打印框架錯誤日志的解決方案
這篇文章主要介紹了springboot log4j2不能打印框架錯誤日志的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Eclipse/MyEclipse轉(zhuǎn)IntelliJ IDEA完全攻略(圖文)
這篇文章主要介紹了Eclipse/MyEclipse轉(zhuǎn)IntelliJ IDEA完全攻略(圖文),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
Java MySQL動態(tài)語句編寫實現(xiàn)方式
這篇文章主要介紹了Java MySQL動態(tài)語句編寫實現(xiàn)方式,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2025-04-04
javacv開發(fā)詳解之調(diào)用本機(jī)攝像頭視頻
這篇文章主要介紹了javacv開發(fā)詳解之調(diào)用本機(jī)攝像頭視頻,對javacv感興趣的同學(xué),可以參考下2021-04-04
Spring Bean作用域詳解之從單例到自定義作用域的全面指南
本文詳細(xì)介紹了Spring框架中的六種標(biāo)準(zhǔn)Bean作用域,包括singleton、prototype、request、session、application和websocket,并探討了如何根據(jù)業(yè)務(wù)需求選擇合適的Bean生命周期管理策略,感興趣的朋友跟隨小編一起看看吧2026-01-01
Spring AOP有多少個通知以及它們的執(zhí)行順序介紹
這篇文章主要介紹了Spring AOP有多少個通知以及它們的執(zhí)行順序,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11

