赞
踩
常见的数据存储库有很多,例如常见的Mysql、Redis、PostgreSql。但在当前敏捷开发的时代,MongoDB不需要设计数据库的结构,省去了设计步骤。在扩展时,支持水平扩展,直接添加新的服务器存储节点,使得系统能够轻松适应大规模的数据和高并发的访问。同样支持索引结构。读了下面的文章,有直截了当的例子,以及实战案例,大家可以直接Ctrl C+V,移植到在自己的代码中。另外例子中富含注释,大家只要花一些时间读一读,都会理解并且快速上手使用。那么下面就由我给大家讲讲。
MongoDB是基于Json数据结构的非关系型数据库,每一个Json文档有一个唯一的主键。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring:
data:
mongodb:
uri: mongodb://username:password@ip:port/dataBaseName
# username、password、ip、port、dataBaseName名称需要写自己的
//利用泛型传参,有效的使代码结构清晰,代码能够重复利用。另外,这里面的增、删、改、查操作都是在添加事务的情况下。 @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事务进行数据一致性的管理。涉及失败回滚操作 * @param entity 添加数据 * @param <S> 数据类型 * @return 添加的数据 */ public <S extends T> S insert(S entity) { ClientSession clientSession = client.startSession(); try { //开始事务 clientSession.startTransaction(); return mongoTemplate.save(entity); } catch (Exception e) { //回滚 clientSession.abortTransaction(); throw new BadRequestException(e.getMessage()); }finally { // 无论是否发生异常,都要结束会话。释放资源 clientSession.close(); } } /** * 主键删除 * * @param s 删除数据 * @param id 主键id * @param <S> 删除数据类型 * @return 删除结果 */ 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 删除数据 * @param ids 主键id * @param <S> 删除数据类型 * @return 删除结果 */ 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 修改数据 * @param id 主键id * @param <S> 修改数据类型 * @return 修改结果 */ 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()); } } /** * 根据条件批量修改 * * @param s 修改数据 * @param query 修改条件 * @param update 修改内容 * @param <S> 修改数据类型 * @return 修改结果 */ 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()); } } /** * 分页查询 * * @param dto 查询条件 * @param s 结果类型 * @param orderName 排序字段 * @param <S> 类型 * @return 分页数据 */ public <S extends T> PageVO pagination(PageRequestDTO dto, S s, String orderName, String orderRules) { return DataToolUtil.pagination(dto, s.getClass(), mongoTemplate, orderName, orderRules); } /** * 根据表id查询数据详情 * * @param id 表id * @param s 数据类型 * @param <S> 返回数据类型 * @return 详情信息 */ public <S extends T> S getInfoById(ID id, S s) { return (S) mongoTemplate.findById(id, s.getClass()); } /** * 根据多个表Id查询信息 * * @param ids 多个表id * @param s 数据类型 * @param <S>返回数据类型 * @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 返回结果 * @param <S> 结果类型 * @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()); } }
@Service
@Slf4j
public class ****ServiceImpl extends BaseService<****Entity, String> implements ****Service {
/**
* 业务代码
*/
}
@Service
@Slf4j
public class ****ServiceImpl extends ServiceImpl<****Mapper, ****Entity> implements *****Service {
/**
* 业务代码
*/
}
Query与Criteria的关联:
Criteria用来构建查询条件,类似于Mybatis-Plus中的wrapper。而Query是用来组合一个或者多个Criteria的。
在构建多条件查询时,使用下面的语句进行合并,合并有两种情况,一个是criteria是根据where构建,另一种是由and构建:
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可以构建包含多个条件的查询。
在使用时与Mybatis-Plus一样需要对实体类进行操作,使用@Id注解指定主键Id,使用@Collation注解来对实体类指定名字。
像这样:
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; //增加一个 @Override public void save(TaskLog tasklog) { this.insert(tasklog);//实际调用在mongoTemplate的save方法 } //批量增加 public void insertMany(List<TaskLog> tasklogs) { mongoTemplate.insert(tasklogs, TaskLog.class); } } /** * mongoTemplate中,insert与save的区别: * insert()方法:如果tasklog中主键id已经存在了,会抛出异常,表示插入了重复的主键。如果该主键不存在,则会正常生成。 * 用于生成新的数据 * * save()方法:如果主键id已经存在,则会覆盖掉原来的数据。 * 用于更新数据 */
@Service @Slf4j public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService { //根据主键id删除一个 @Override public void delete(String logId) { this.deleteById(new TaskLog(), logId); } //根据主键id批量删除 public void deleteMany(List<String> logIds) { this.deleteByIds(new TaskLog(), logIds); } } /** * 实际使用的是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 { //根据主键id修改 @Override public void update(String logId) { this.update(data, data.getId()); } } /** * 实际使用的是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); * } * } * 利用反射覆盖对应实体类中的每一个字段以及字段值。 * mongoTemplate.updateFirst(query, update, s.getClass()); */
@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条件的数据。 //如果构建的query为null,listByQuery()会调用findAll()方法,查询所有的数据 }
分页查询(重点):
@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"); //第一个参数:查询条件 //第二个参数:结果类型 //第三个参数:排序字段 //第四个参数:排序类型,升序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); }
//分页查询的使用封装成了一个工具类 @Slf4j public class DataToolUtil { //防止工具类被实例化 private DataToolUtil() { throw new IllegalStateException("Utility class"); } /** * 在mongodb中分页查询,通过 PageVO 对象封装了结果数量和查询结果 * * @param dto 分页条件 * @param tClass 返回数据类型 * @param mongoTemplate 查询模板 * @param <T> 返回数据 * @param orderName 排序列名 * @param orderRules 排序规则 asc 正序 desc 倒序 * @return 分页结果 */ 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())) {//没有请求的页码,默认是第一页 dto.setCurrentPageNum(0); } if (!ObjectUtils.allNotNull(dto.getSize())) {//没有每页展示的数量,默认是展示10个 dto.setSize(10); } //用给定的查询对象query来计算文档数量 long count = mongoTemplate.count(query, tClass); /** * dto.getCurrentPageNum():用户请求的页码 * dto.getSize():每页展示的数量 * offset用来计算偏移量 * 例如用户请求的是第2页,(2-1)=1。每页展示10个,1*10=10.则从第11个开始展示。 */ 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))); } } // 分页逻辑 /** * 举例:跳过10个,每页获取10个,展示11~20的数据。 */ query.skip(offset).limit(dto.getSize()); log.info("querySql>>>>" + query); //find()用于获取符合query查询条件的文档列表.tClass是目标实体类 List<T> list = mongoTemplate.find(query, tClass); //创建一个用于封装分页查询结果的对象 PageVO<T> pageDTO = new PageVO<>(); //给PageVO对象设置查询到的满足条件的数量。 pageDTO.setTotalNum(count); //将查询到的结果设置到PageVO对象中。 pageDTO.setResults(list); return pageDTO; } /** * 根据传入的查询条件进行 MongoDB 数据库的动态查询 * @param dto * @return */ private static Query handParam(PageRequestDTO dto) { Query query = new Query();//创建一个MongoDB查询对象 List<ParamDTO> params = dto.getParams();//获取传入参数的查询条件列表 Criteria criteria = new Criteria();//创建MongoDB的Criteria对象,用于构建查询条件 params.forEach(param -> {//遍历查询条件列表 String operator = param.getOperator(); switch (operator) { //该语句构建了一个等于(eq)的查询条件,使用 is 方法指定字段等于给定的值。 case "eq" -> criteria.and(param.getFiled()).is(param.getFiledValue()); //regex表示使用正则表达式来进行查询,Pattern.compile()来构造一个正则表达式。这个正则表达式是‘^.*value.*$’ //'^'和‘$’是正则表达式的起始和结束标识。‘.*表示匹配字符0次或者多次’ //Pattern.CASE_INSENSITIVE表示不区分大小写 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()); //转换成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 -> {//没匹配任何一个case语句,默认执行default语句。 } } }); query.addCriteria(criteria);//将构建好的 Criteria 对象添加到查询对象中。 return query; } }
@Data
public class PageVO<T> {
private Long totalNum;//结果总数
private List<T>results;//查询结果
}
@Data
@Schema(description = "分页查询条件")
public class PageRequestDTO {
@Schema(description = "当前页数")
private Integer currentPageNum;
@Schema(description = "展示行数")
private Integer size;
@Schema(description = "查询条件")
private List<ParamDTO> params;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。