MongoDB在Spring Boot中的使用方式舉例介紹
引言:
常見的數(shù)據(jù)存儲(chǔ)庫(kù)有很多,例如常見的Mysql、Redis、PostgreSql。但在當(dāng)前敏捷開發(fā)的時(shí)代,MongoDB不需要設(shè)計(jì)數(shù)據(jù)庫(kù)的結(jié)構(gòu),省去了設(shè)計(jì)步驟。在擴(kuò)展時(shí),支持水平擴(kuò)展,直接添加新的服務(wù)器存儲(chǔ)節(jié)點(diǎn),使得系統(tǒng)能夠輕松適應(yīng)大規(guī)模的數(shù)據(jù)和高并發(fā)的訪問。同樣支持索引結(jié)構(gòu)。讀了下面的文章,有直截了當(dāng)?shù)睦樱约皩?shí)戰(zhàn)案例,大家可以直接Ctrl C+V,移植到在自己的代碼中。另外例子中富含注釋,大家只要花一些時(shí)間讀一讀,都會(huì)理解并且快速上手使用。那么下面就由我給大家講講。
MongoDB的形式:
MongoDB是基于Json數(shù)據(jù)結(jié)構(gòu)的非關(guān)系型數(shù)據(jù)庫(kù),每一個(gè)Json文檔有一個(gè)唯一的主鍵。
一、在SpringBoot項(xiàng)目中整合MongoDB,首先需要引入Pom文件依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>二、在yaml配置文件中進(jìn)行配置,Spring會(huì)自動(dòng)掃描配置文件,并且利用。
spring:
data:
mongodb:
uri: mongodb://username:password@ip:port/dataBaseName
# username、password、ip、port、dataBaseName名稱需要寫自己的三、使用上,我們定義一個(gè)Service類,里面封裝好Mongo操作,在這里將這個(gè)Mongo Service開放給大家,方便大家使用:
//利用泛型傳參,有效的使代碼結(jié)構(gòu)清晰,代碼能夠重復(fù)利用。另外,這里面的增、刪、改、查操作都是在添加事務(wù)的情況下。
@Service
public class BaseService<T, ID> {
private MongoTemplate mongoTemplate;
private MongoClient client;
@Resource
public void setClient(MongoClient client) {
this.client = client;
}
@Resource
public void setMongoTemplate(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
/**
* MongoDB事務(wù)進(jìn)行數(shù)據(jù)一致性的管理。涉及失敗回滾操作
* @param entity 添加數(shù)據(jù)
* @param <S> 數(shù)據(jù)類型
* @return 添加的數(shù)據(jù)
*/
public <S extends T> S insert(S entity) {
ClientSession clientSession = client.startSession();
try {
//開始事務(wù)
clientSession.startTransaction();
return mongoTemplate.save(entity);
} catch (Exception e) {
//回滾
clientSession.abortTransaction();
throw new BadRequestException(e.getMessage());
}finally {
// 無(wú)論是否發(fā)生異常,都要結(jié)束會(huì)話。釋放資源
clientSession.close();
}
}
/**
* 主鍵刪除
*
* @param s 刪除數(shù)據(jù)
* @param id 主鍵id
* @param <S> 刪除數(shù)據(jù)類型
* @return 刪除結(jié)果
*/
public <S extends T> DeleteResult deleteById(S s, ID id) {
ClientSession clientSession = client.startSession();
try {
clientSession.startTransaction();
Query query = Query.query(Criteria.where("_id").is(id));
return mongoTemplate.remove(query, s.getClass());
} catch (Exception e) {
clientSession.abortTransaction();
throw new BadRequestException(e.getMessage());
}
}
/**
* 批量刪除
*
* @param s 刪除數(shù)據(jù)
* @param ids 主鍵id
* @param <S> 刪除數(shù)據(jù)類型
* @return 刪除結(jié)果
*/
public <S extends T> DeleteResult deleteByIds(S s, List<ID> ids) {
ClientSession clientSession = client.startSession();
try {
clientSession.startTransaction();
Query query = Query.query(Criteria.where("_id").in(ids));
return mongoTemplate.remove(query, s.getClass());
} catch (Exception e) {
clientSession.abortTransaction();
throw new BadRequestException(e.getMessage());
}
}
/**
* 修改
*
* @param s 修改數(shù)據(jù)
* @param id 主鍵id
* @param <S> 修改數(shù)據(jù)類型
* @return 修改結(jié)果
*/
public <S extends T> UpdateResult update(S s, String id) {
ClientSession clientSession = client.startSession();
try {
clientSession.startTransaction();
Query query = Query.query(Criteria.where("_id").is(id));
Update update = new Update();
Field[] fields = s.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//獲取屬性
String name = field.getName();
//獲取屬性值
Object value = field.get(s);
if (ObjectUtils.allNotNull(value)) {
update.set(name, value);
}
}
return mongoTemplate.updateFirst(query, update, s.getClass());
} catch (Exception e) {
clientSession.abortTransaction();
throw new BadRequestException(e.getMessage());
}
}
/**
* 根據(jù)條件批量修改
*
* @param s 修改數(shù)據(jù)
* @param query 修改條件
* @param update 修改內(nèi)容
* @param <S> 修改數(shù)據(jù)類型
* @return 修改結(jié)果
*/
public <S extends T> UpdateResult updateByQuery(S s, Query query, Update update) {
ClientSession clientSession = client.startSession();
try {
clientSession.startTransaction();
return mongoTemplate.updateMulti(query, update, s.getClass());
} catch (Exception e) {
clientSession.abortTransaction();
throw new BadRequestException(e.getMessage());
}
}
/**
* 分頁(yè)查詢
*
* @param dto 查詢條件
* @param s 結(jié)果類型
* @param orderName 排序字段
* @param <S> 類型
* @return 分頁(yè)數(shù)據(jù)
*/
public <S extends T> PageVO pagination(PageRequestDTO dto, S s, String orderName, String orderRules) {
return DataToolUtil.pagination(dto, s.getClass(), mongoTemplate, orderName, orderRules);
}
/**
* 根據(jù)表id查詢數(shù)據(jù)詳情
*
* @param id 表id
* @param s 數(shù)據(jù)類型
* @param <S> 返回?cái)?shù)據(jù)類型
* @return 詳情信息
*/
public <S extends T> S getInfoById(ID id, S s) {
return (S) mongoTemplate.findById(id, s.getClass());
}
/**
* 根據(jù)多個(gè)表Id查詢信息
*
* @param ids 多個(gè)表id
* @param s 數(shù)據(jù)類型
* @param <S>返回?cái)?shù)據(jù)類型
* @return 詳情集合
*/
public <S extends T> List<S> getInfoByIds(List<ID> ids, S s) {
Query query = Query.query(Criteria.where("_id").in(ids));
return (List<S>) mongoTemplate.find(query, s.getClass());
}
/**
* 條件查詢
*
* @param query 查詢條件
* @param s 返回結(jié)果
* @param <S> 結(jié)果類型
* @return 列表
*/
public <S extends T> List<S> listByQuery(Query query, S s) {
if (null != query) {
return (List<S>) mongoTemplate.find(query, s.getClass());
}
return (List<S>) mongoTemplate.findAll(s.getClass());
}
}四、然后在要使用Mongo的業(yè)務(wù)類繼承這個(gè)Service類,像這樣:
@Service
@Slf4j
public class ****ServiceImpl extends BaseService<****Entity, String> implements ****Service {
/**
* 業(yè)務(wù)代碼
*/
}類似于使用Mybatis-Plus,要使用MP中Service層級(jí)的方法,需要繼承ServiceImpl<Mapper, Entity>,像這樣:
@Service
@Slf4j
public class ****ServiceImpl extends ServiceImpl<****Mapper, ****Entity> implements *****Service {
/**
* 業(yè)務(wù)代碼
*/
}五、具體在業(yè)務(wù)類中如何使用,我在這里舉一些例子來(lái)進(jìn)行說明,幫助大家最大程度的理解,并可以快速上手使用。
Query與Criteria的關(guān)聯(lián):
Criteria用來(lái)構(gòu)建查詢條件,類似于Mybatis-Plus中的wrapper。而Query是用來(lái)組合一個(gè)或者多個(gè)Criteria的。
在構(gòu)建多條件查詢時(shí),使用下面的語(yǔ)句進(jìn)行合并,合并有兩種情況,一個(gè)是criteria是根據(jù)where構(gòu)建,另一種是由and構(gòu)建:
Query query = new Query();
Criteria criteria1 = Criteria.where("_id").is(id);
query.addCriteria(criteria1);//where形式
Criteria criteria2 = new Criteria();
criteria2.and("_id").is(id);//and形式
query.addCriteria(criteria2);where是單一條件查詢,而and可以構(gòu)建包含多個(gè)條件的查詢。
在使用時(shí)與Mybatis-Plus一樣需要對(duì)實(shí)體類進(jìn)行操作,使用@Id注解指定主鍵Id,使用@Collation注解來(lái)對(duì)實(shí)體類指定名字。
像這樣:
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.annotation.Collation;
import java.io.Serializable;
@Data
@Collation("taskLog")
public class TaskLog implements Serializable {
@Id
private String logId;
/*
* 其他屬性
*/
}增:
@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
@Autowired
private MongoTemplate mongoTemplate;
//增加一個(gè)
@Override
public void save(TaskLog tasklog) {
this.insert(tasklog);//實(shí)際調(diào)用在mongoTemplate的save方法
}
//批量增加
public void insertMany(List<TaskLog> tasklogs) {
mongoTemplate.insert(tasklogs, TaskLog.class);
}
}
/**
* mongoTemplate中,insert與save的區(qū)別:
* insert()方法:如果tasklog中主鍵id已經(jīng)存在了,會(huì)拋出異常,表示插入了重復(fù)的主鍵。如果該主鍵不存在,則會(huì)正常生成。
* 用于生成新的數(shù)據(jù)
*
* save()方法:如果主鍵id已經(jīng)存在,則會(huì)覆蓋掉原來(lái)的數(shù)據(jù)。
* 用于更新數(shù)據(jù)
*/刪:
@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
//根據(jù)主鍵id刪除一個(gè)
@Override
public void delete(String logId) {
this.deleteById(new TaskLog(), logId);
}
//根據(jù)主鍵id批量刪除
public void deleteMany(List<String> logIds) {
this.deleteByIds(new TaskLog(), logIds);
}
}
/**
* 實(shí)際使用的是mongoTemplate的remove()方法。
* Query query = Query.query(Criteria.where("_id").is(id));
* mongoTemplate.remove(query, s.getClass());
*
* Query query = Query.query(Criteria.where("_id").in(ids));
* mongoTemplate.remove(query, s.getClass());
*/改:
@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
//根據(jù)主鍵id修改
@Override
public void update(String logId) {
this.update(data, data.getId());
}
}
/**
* 實(shí)際使用的是mongoTemplate中的updateFirst()方法
* Query query = Query.query(Criteria.where("_id").is(id));
* Update update = new Update();
* Field[] fields = s.getClass().getDeclaredFields();
* for (Field field : fields) {
* field.setAccessible(true);
* //獲取屬性
* String name = field.getName();
* //獲取屬性值
* Object value = field.get(s);
* if (ObjectUtils.allNotNull(value)) {
* update.set(name, value);
* }
* }
* 利用反射覆蓋對(duì)應(yīng)實(shí)體類中的每一個(gè)字段以及字段值。
* mongoTemplate.updateFirst(query, update, s.getClass());
*/簡(jiǎn)單查:
@Service
@Slf4j
public class AnalysisStudyServiceImpl extends BaseService<AnalysisStudy, String> implements AnalysisStudyService {
private MongoTemplate mongoTemplate;
@Resource
public void setMongoTemplate(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
/**
* 條件查詢
*/
@Override
public void find(String patientId, String hospitalId) {
Query query = Query.query(Criteria.where("patientId").is(patientId).and("hospitalId").is(hospitalId));
List<AnalysisStudy> analysisStudyList = this.listByQuery(query, new AnalysisStudy());
}
//底層使用的是mongoTemplate的find()方法,查詢滿足query條件的數(shù)據(jù)。
//如果構(gòu)建的query為null,listByQuery()會(huì)調(diào)用findAll()方法,查詢所有的數(shù)據(jù)
}分頁(yè)查詢(重點(diǎn)):
@Service
@Slf4j
public class AnalysisStudyServiceImpl extends BaseService<AnalysisStudy, String> implements AnalysisStudyService {
@Override
public PageVO<AnalysisStudy> queryPage(PageRequestDTO pageRequest) {
return this.pagination(pageRequest, new AnalysisStudy(), "studyDateTime", "asc");
//第一個(gè)參數(shù):查詢條件
//第二個(gè)參數(shù):結(jié)果類型
//第三個(gè)參數(shù):排序字段
//第四個(gè)參數(shù):排序類型,升序ASC或者降序DESC
}
}
//BaseService中封裝的方法
public <S extends T> PageVO pagination(PageRequestDTO dto, S s, String orderName, String orderRules) {
return DataToolUtil.pagination(dto, s.getClass(), mongoTemplate, orderName, orderRules);
}//分頁(yè)查詢的使用封裝成了一個(gè)工具類
@Slf4j
public class DataToolUtil {
//防止工具類被實(shí)例化
private DataToolUtil() {
throw new IllegalStateException("Utility class");
}
/**
* 在mongodb中分頁(yè)查詢,通過 PageVO 對(duì)象封裝了結(jié)果數(shù)量和查詢結(jié)果
*
* @param dto 分頁(yè)條件
* @param tClass 返回?cái)?shù)據(jù)類型
* @param mongoTemplate 查詢模板
* @param <T> 返回?cái)?shù)據(jù)
* @param orderName 排序列名
* @param orderRules 排序規(guī)則 asc 正序 desc 倒序
* @return 分頁(yè)結(jié)果
*/
public static <T> PageVO<T> pagination(PageRequestDTO dto, Class<T> tClass,
MongoTemplate mongoTemplate,
String orderName, String orderRules) {
Query query = handParam(dto);
if (!ObjectUtils.allNotNull(dto.getCurrentPageNum())) {//沒有請(qǐng)求的頁(yè)碼,默認(rèn)是第一頁(yè)
dto.setCurrentPageNum(0);
}
if (!ObjectUtils.allNotNull(dto.getSize())) {//沒有每頁(yè)展示的數(shù)量,默認(rèn)是展示10個(gè)
dto.setSize(10);
}
//用給定的查詢對(duì)象query來(lái)計(jì)算文檔數(shù)量
long count = mongoTemplate.count(query, tClass);
/**
* dto.getCurrentPageNum():用戶請(qǐng)求的頁(yè)碼
* dto.getSize():每頁(yè)展示的數(shù)量
* offset用來(lái)計(jì)算偏移量
* 例如用戶請(qǐng)求的是第2頁(yè),(2-1)=1。每頁(yè)展示10個(gè),1*10=10.則從第11個(gè)開始展示。
*/
int offset = (dto.getCurrentPageNum() - 1) * dto.getSize();
if (StringUtils.isNotEmpty(orderName) && StringUtils.isNotEmpty(orderRules)) {
String asc = "asc";//升序排序
String desc = "desc";//降序排序
if (asc.equals(orderRules)) {
//排序邏輯
query.with(Sort.by(Sort.Order.asc(orderName)));
}
if (desc.equals(orderRules)) {
//排序邏輯
query.with(Sort.by(Sort.Order.desc(orderName)));
}
}
// 分頁(yè)邏輯
/**
* 舉例:跳過10個(gè),每頁(yè)獲取10個(gè),展示11~20的數(shù)據(jù)。
*/
query.skip(offset).limit(dto.getSize());
log.info("querySql>>>>" + query);
//find()用于獲取符合query查詢條件的文檔列表.tClass是目標(biāo)實(shí)體類
List<T> list = mongoTemplate.find(query, tClass);
//創(chuàng)建一個(gè)用于封裝分頁(yè)查詢結(jié)果的對(duì)象
PageVO<T> pageDTO = new PageVO<>();
//給PageVO對(duì)象設(shè)置查詢到的滿足條件的數(shù)量。
pageDTO.setTotalNum(count);
//將查詢到的結(jié)果設(shè)置到PageVO對(duì)象中。
pageDTO.setResults(list);
return pageDTO;
}
/**
* 根據(jù)傳入的查詢條件進(jìn)行 MongoDB 數(shù)據(jù)庫(kù)的動(dòng)態(tài)查詢
* @param dto
* @return
*/
private static Query handParam(PageRequestDTO dto) {
Query query = new Query();//創(chuàng)建一個(gè)MongoDB查詢對(duì)象
List<ParamDTO> params = dto.getParams();//獲取傳入?yún)?shù)的查詢條件列表
Criteria criteria = new Criteria();//創(chuàng)建MongoDB的Criteria對(duì)象,用于構(gòu)建查詢條件
params.forEach(param -> {//遍歷查詢條件列表
String operator = param.getOperator();
switch (operator) {
//該語(yǔ)句構(gòu)建了一個(gè)等于(eq)的查詢條件,使用 is 方法指定字段等于給定的值。
case "eq" -> criteria.and(param.getFiled()).is(param.getFiledValue());
//regex表示使用正則表達(dá)式來(lái)進(jìn)行查詢,Pattern.compile()來(lái)構(gòu)造一個(gè)正則表達(dá)式。這個(gè)正則表達(dá)式是‘^.*value.*$'
//'^'和‘$'是正則表達(dá)式的起始和結(jié)束標(biāo)識(shí)。‘.*表示匹配字符0次或者多次'
//Pattern.CASE_INSENSITIVE表示不區(qū)分大小寫
case "like" -> criteria.and(param.getFiled()).regex(Pattern.compile("^.*" + param.getFiledValue()
+ ".*$", Pattern.CASE_INSENSITIVE));
case "le" -> criteria.and(param.getFiled()).lt(param.getFiledValue());
case "ge" -> criteria.and(param.getFiled()).gt(param.getFiledValue());
//轉(zhuǎn)換成String類型的字符串列表
case "in" -> {
List<String> values = JSON.parseArray(param.getFiledValue().toString(), String.class);
criteria.and(param.getFiled()).in(values);
}
case "between" -> {
if (param.getFiledValue() instanceof JSONArray) {
List<Object> list = JSON.parseArray(param.getFiledValue().toString(), Object.class);
if (CollUtil.isNotEmpty(list)) {
//gte:大于等于
//lte:小于等于
criteria.and(param.getFiled()).gte(list.get(0)).lte(list.get(1));
}
}
}
default -> {//沒匹配任何一個(gè)case語(yǔ)句,默認(rèn)執(zhí)行default語(yǔ)句。
}
}
});
query.addCriteria(criteria);//將構(gòu)建好的 Criteria 對(duì)象添加到查詢對(duì)象中。
return query;
}
}@Data
public class PageVO<T> {
private Long totalNum;//結(jié)果總數(shù)
private List<T>results;//查詢結(jié)果
}@Data
@Schema(description = "分頁(yè)查詢條件")
public class PageRequestDTO {
@Schema(description = "當(dāng)前頁(yè)數(shù)")
private Integer currentPageNum;
@Schema(description = "展示行數(shù)")
private Integer size;
@Schema(description = "查詢條件")
private List<ParamDTO> params;
}到此這篇關(guān)于MongoDB在Spring Boot中的使用方式舉例介紹的文章就介紹到這了,更多相關(guān)MongoDB Spring Boot使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Socket+多線程實(shí)現(xiàn)多人聊天室功能
這篇文章主要為大家詳細(xì)介紹了Java Socket+多線程實(shí)現(xiàn)多人聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Java讀取項(xiàng)目json文件并轉(zhuǎn)為JSON對(duì)象的操作
這篇文章主要介紹了Java讀取項(xiàng)目json文件并轉(zhuǎn)為JSON對(duì)象的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
從Mybatis-Plus開始認(rèn)識(shí)SerializedLambda的詳細(xì)過程
這篇文章主要介紹了從Mybatis-Plus開始認(rèn)識(shí)SerializedLambda,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07
SpringBoot項(xiàng)目啟動(dòng)執(zhí)行任務(wù)的多種方法小結(jié)
這篇文章主要介紹了SpringBoot項(xiàng)目啟動(dòng)執(zhí)行任務(wù)的多種方法小結(jié),本文給大家分享的這幾種方法經(jīng)常會(huì)被用到,當(dāng)我們的項(xiàng)目啟動(dòng)后需要調(diào)用對(duì)應(yīng)的方法,用來(lái)項(xiàng)目的初始化等,本文通過示例代碼講解的非常詳細(xì),需要的朋友參考下吧2023-07-07
Springboot使用ResponseBody漢字返回問號(hào)問題
這篇文章主要介紹了Springboot使用ResponseBody漢字返回問號(hào)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06

