介绍 MongoTemplate 与 MongoRepository 对 MongoDB 数据的操作
码云地址:
https://gitee.com/dmbjzorg/Springboot_Mongdb
配置:
创建SpringBoot项目,勾选 MongoDB依赖进行搭建
勾选必要的依赖
填写配置文件:
spring.data.mongodb.host=192.168.182.129spring.data.mongodb.port=27017spring.data.mongodb.database=dmbjz# spring.data.mongodb.uri:http://192.168.182.129:27017/dmbjz 使用URL方式直接连接
创建实体类:
@Document(value = "springboot") //指定使用的集合名称,如果不填将调用类名小写集合@CompoundIndex(def = "{'userid'=1,'likenum'=1}") //指定使用聚合索引的字段@Data@AllArgsConstructor@NoArgsConstructor@Accessors(chain = true)public class Comment implements Serializable {@Idprivate String id; //主键,注解意为对应 _id,如果数据的主键字段不为 _id需要添加@Id注解@Field("content")private String contents; //评论内容,注解对应mongodb的字段的名字,如果一致,则无需该注解@Indexedprivate String userid; //发布人ID,注解意为使用单字段索引private Date publishtime; //发布日期private String nickname; //昵称private LocalDateTime createdatetime; //评论的日期时间private Integer likenum; //点赞数private Integer replynum; //回复数private String state; //状态public Comment(String contents, String userid, Date publishtime, String nickname, LocalDateTime createdatetime, Integer likenum, Integer replynum, String state) {this.contents = contents;this.userid = userid;this.publishtime = publishtime;this.nickname = nickname;this.createdatetime = createdatetime;this.likenum = likenum;this.replynum = replynum;this.state = state;}}
添加配置文件:
用于去除默认保存实体类对象的会创建的 _class 字段
import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.NoSuchBeanDefinitionException;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.mongodb.MongoDatabaseFactory;import org.springframework.data.mongodb.core.convert.*;import org.springframework.data.mongodb.core.mapping.MongoMappingContext;@Configurationpublic class MongoConfig {@Beanpublic MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory factory, MongoMappingContext context, BeanFactory beanFactory) {DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);try {mappingConverter.setCustomConversions(beanFactory.getBean(MongoCustomConversions.class));} catch (NoSuchBeanDefinitionException ignore) {}mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));return mappingConverter;}}
MongoTemplate 案例:
对数据的操作都是基于 MongoTemplate 进行完成,对数据的查询更新需要使用 MongoDB自己的数据类型,操作类似 MybatisPlus
添加数据:
/** MongoTemplate 操作数据案例 */@RestControllerpublic class HelloController {@Autowiredprivate MongoTemplate template;/*保存*/@RequestMapping("save")public void saveComment(){List<Comment> tempList = new ArrayList<>(3);tempList.add(new Comment("1","今天天气还行","dmbjz",new Date(),"游客A", LocalDateTime.now(),200,200,"1"));tempList.add(new Comment("2","手机今天欠费","dmbjz",new Date(),"游客B", LocalDateTime.now(),80,200,"1"));tempList.add(new Comment("3","你打算在今天干嘛","dmbjz",new Date(),"游客C", LocalDateTime.now(),170,200,"1"));tempList.add(new Comment("4","我觉得明晚不错","cctv",new Date(),"游客D", LocalDateTime.now(),100,200,"1"));tempList.add(new Comment("5","要学习知识","acfun",new Date(),"游客E", LocalDateTime.now(),100,200,"1"));template.insert(tempList,Comment.class); //批量保存Comment comment = new Comment("额外添加的数据","acfun",new Date(),"游客Z", LocalDateTime.now(),500,30,"1");template.save(comment); //save只能保存单个数据}}

查询数据:
查询条件需要使用 Query 类进行包装,如果遇到模糊查询需要使用 Pattern 类包装查询条件
@RequestMapping("find")public void findCommentList(Long page){//查询全部数据List<Comment> findList = template.findAll(Comment.class);findList.forEach(System.out::println);//根据ID查询Comment getId = template.findById("1", Comment.class);System.out.println("根据ID查询到的数据:"+getId);/*查询以 userid为dmbjz + likenum大于等于100 + content包含“还行”的数据*/System.out.println("------------开始条件查询------------");Query query = new Query(Criteria.where("userid").is("dmbjz").and("likenum").gte(100));//使用正则表达式进行匹配,Pattern.CASE_INSENSITIVE :不区分大小写匹配Pattern pattern = Pattern.compile(".*还行.*", Pattern.CASE_INSENSITIVE);query.addCriteria(Criteria.where("contents").regex(pattern));List<Comment> comments = template.find(query, Comment.class);comments.forEach(System.out::println);//查询数据总量long count = template.count(new Query(), Comment.class);System.out.println("springboot集合中当前文档总数为: " + count);/** 分页查询* 3 相当于 pageSize* (page - 1) 相当于当前页码*/System.out.println("------------开始分页查询------------");List<Comment> pageList = template.find(new Query().skip((page - 1) * 3).limit(3), Comment.class);pageList.forEach(System.out::println);}
删除数据:
/*删除id不为0的数据*/@RequestMapping("del")public void deleteCommentById(String id){Query query = new Query(Criteria.where("id").nin("0"));template.remove(query,Comment.class);}
修改数据:
/*更新*/@RequestMapping("update")public void updateComment(){//批量更新Query query = new Query(Criteria.where("userid").is("dmbjz"));Update update = new Update().set("nickname","修改的名称").set("replynum",678);UpdateResult updateResult = template.updateMulti(query, update, Comment.class);if(updateResult.getModifiedCount()>0){System.out.println("批量修改成功!");}//单个更新,更新最先查询到的数据Query query2 = new Query(Criteria.where("userid").is("acfun"));UpdateResult updateResult2 = template.upsert(query2, update, Comment.class);if(updateResult2.getModifiedCount()>0){System.out.println("单独修改成功!");}}

聚合操作:
类似于 MySQL 中的 GroupBy 聚合操作
/*分组操作*/@RequestMapping("group")public void groupFind(){Criteria criteria = Criteria.where("likenum").gt(80);//根据工作日进行分组(聚合操作)Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(criteria), //查询条件,相当于MySQL中的 Where;这里为 likenum > 80Aggregation.group("userid") //Myngodb里数据的分组字段,这里为根据 userid 进行分组.first("contents").as("contentsShow") //获取group by 后的某个字段的首个值。AS 用于查询返回的别名,类似于MySQL中的 AS "xxxx".first("userid").as("userIdShow") //获取grou by 后的userId字段的首个值,返回别名 "userIdShow".count().as("userCount") //获取分组后的数量.sum("replynum").as("replyNumShow") //统计 replynum 和,相当于 sum(replynum) AS "replynum".sum("likenum").as("likeNumShow"), //统计 likenum 数,相当于 sum(likenum) AS "replynum"Aggregation.sort(Sort.Direction.ASC,"replyNumShow"), //根据 replyNumShow 进行升序//Aggregation.skip(1), //跳过数据,可用于分页起始数据Aggregation.limit(3) //相当于 MySQL里的 Limit ,分页结束);//聚合查询,参数一:聚合条件,参数二:查询数据对应实体类,参数三:返回结果ClassAggregationResults<CommentVo> listResult = template.aggregate(aggregation, Comment.class, CommentVo.class);//获取返回数据List<CommentVo> infoList = listResult.getMappedResults();infoList.forEach(System.out::println);}

MongoRepository 案例:
MongoRepository 相较于 MongoTemplate 操作更为简单,需要继承 MongoRepository
每操作一个不同的实体类就需要定义一个 MongoRepository,且模糊查询操作没有 MongoTemplate 灵活便捷
创建 UserRepository:
package com.dmbjz.repository;import com.dmbjz.entity.Comment;import org.springframework.data.mongodb.repository.MongoRepository;import org.springframework.stereotype.Repository;/*** 使用 MongoRepository 代替 MongoTemplate 操作数据* 相较于 MongoTemplate,每操作一个不同的实体类就需要定义一个 MongoRepository* 参数:操作实体类,Example查询操作类型*/@Repositorypublic interface UserRepository extends MongoRepository<Comment, String> {}
保存数据:
/** MongoRepository 操作数据案例 */@RestController@RequestMapping("repository")public class HelloRepository {@Autowiredprivate UserRepository userRepository;/*保存*/@RequestMapping("save")public void saveComment(){List<Comment> tempList = new ArrayList<>(3);tempList.add(new Comment("1","今天天气还行","dmbjz",new Date(),"游客A", LocalDateTime.now(),200,200,"1"));tempList.add(new Comment("2","手机今天欠费","dmbjz",new Date(),"游客B", LocalDateTime.now(),80,200,"1"));tempList.add(new Comment("3","你打算在今天干嘛","dmbjz",new Date(),"游客C", LocalDateTime.now(),170,200,"1"));tempList.add(new Comment("4","我觉得明晚不错","cctv",new Date(),"游客D", LocalDateTime.now(),100,200,"1"));tempList.add(new Comment("5","要学习知识","acfun",new Date(),"游客E", LocalDateTime.now(),100,200,"1"));userRepository.saveAll(tempList); //批量保存Comment comment = new Comment("额外添加的数据","acfun",new Date(),"游客Z", LocalDateTime.now(),500,30,"1");userRepository.save(comment); //保存单个数据}}

查询数据:
/*查询*/@RequestMapping("find")public void findCommentList(){//查询全部数据List<Comment> findList = userRepository.findAll();findList.forEach(System.out::println);//根据ID查询Optional<Comment> info = userRepository.findById("1");info.ifPresent(a->System.out.println("根据ID查询到的数据:"+ a.getId()));/** 精确查询* 查询所有结果为 userid为dmbjz + likenum等于100 的数据*/Comment comment = new Comment();comment.setUserid("dmbjz");comment.setLikenum(100);Example<Comment> userExample = Example.of(comment);List<Comment> comments = userRepository.findAll(userExample);comments.forEach(System.out::println);/** 模糊查询* 缺点:只能对所有条件字段统一进行某种特定的搜索,但数值类型只能进行精确匹配* 例:查询 userid包含dmbjz + likenum等于20 + content包含“还行”的数据*/System.out.println("========== 模糊搜索案例 START ============");ExampleMatcher matcher = ExampleMatcher.matching() //构建匹配器.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询.withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写Comment comment2 = new Comment();comment2.setUserid("dmbjz");comment2.setLikenum(20); //数值类型只能精确查询comment2.setContents("还行");Example<Comment> userExample2 = Example.of(comment2, matcher);List<Comment> userList = userRepository.findAll(userExample2);System.out.println(userList);System.out.println("========== 模糊搜索案例 END ============");//查询数据总量long count = userRepository.count();System.out.println("springboot集合中当前文档总数为: " + count);/** 分页查询*/System.out.println("========== 分页查询 START ============");Sort sort = Sort.by(Sort.Direction.DESC, "age"); //根据Age字段倒序Pageable pageable = PageRequest.of(0, 10, sort); //创建分页对象,参数: 当前页索引(第1页index 0,第二页index 1....)、数据显示数、排序ExampleMatcher pageMatcher = ExampleMatcher.matching() //构建匹配器.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询.withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写Comment temp = new Comment();temp.setNickname("游客A");Example<Comment> pageExample = Example.of(temp, pageMatcher); //创建实例Page<Comment> pages = userRepository.findAll(pageExample, pageable); //分页查询System.out.println("总页数:"+pages.getTotalPages());System.out.println("数据列表:"+pages.getContent());System.out.println("是否有下一页:"+pages.hasNext());System.out.println("========== 分页查询 END ============");}

删除数据:
无论是 delete 方法还是 deletAll 方法,其内部参数都需要携带主键ID字段,否则会在 getByIdQuery() 中抛出异常,因此使用 deleteById 方法即可
/*删除*/@RequestMapping("del")public void deleteCommentById(){userRepository.deleteById("3"); //删除 id = 3 的数据}

更新数据:
需要先查询再更新数据,如果没有先查询将会进行覆盖更新
更新操作依赖主键ID字段
/*更新*/@RequestMapping("update")public void updateComment(){//单个更新,先查询再更新数据,需要依赖主键ID字段Optional<Comment> findComment = userRepository.findById("4");findComment.ifPresent(info->{info.setNickname("dmbjz_new");info.setContents("MongoRepository更新操作");info.setLikenum(999);userRepository.save(info);});//批量更新,需要依赖主键ID字段List<Comment> upList = new ArrayList<>(2);Comment temp = new Comment().setId("1").setContents("今天天气不行");Comment temp2 = new Comment().setId("2").setContents("今天手机交话费了");upList.add(temp);upList.add(temp2);userRepository.saveAll(upList);}

SpringData 快捷查询支持:
通过在 MongoRepository 接口中使用 SpringData 规范直接定义查询方法
注意事项:
1、不是随便声明的,而需要符合一定的规范
2、 查询方法以find | read | get开头
3、 涉及条件查询时,条件的属性用条件关键字连接
4、 要注意的是:条件属性首字母需要大写
5、 支持属性的级联查询,但若当前类有符合条件的属性则优先使用,而不使用级联属性,若需要使用级联属性,则属性之间使用 “_” 强制进行连接

修改 UserRepository:
@Repositorypublic interface UserRepository extends MongoRepository<Comment, String> {//SpringData规范查询数据方法一Comment findByContentsAndLikenum(String contents,Integer likenum);//SpringData规范查询数据方法二List<Comment> readAllByUserid(String nickname);}
查询测试:
/* SpringData规范查询数据 */@RequestMapping("data")public void springData(){Comment info = userRepository.findByContentsAndLikenum("要学习知识", 100);System.out.println(info);List<Comment> infoList = userRepository.readAllByUserid("dmbjz");infoList.forEach(System.out::println);}



