介绍 MongoTemplate 与 MongoRepository 对 MongoDB 数据的操作

码云地址:

  1. https://gitee.com/dmbjzorg/Springboot_Mongdb

配置:

创建SpringBoot项目,勾选 MongoDB依赖进行搭建
image.png
勾选必要的依赖

填写配置文件:

  1. spring.data.mongodb.host=192.168.182.129
  2. spring.data.mongodb.port=27017
  3. spring.data.mongodb.database=dmbjz
  4. # spring.data.mongodb.uri:http://192.168.182.129:27017/dmbjz 使用URL方式直接连接

创建实体类:

  1. @Document(value = "springboot") //指定使用的集合名称,如果不填将调用类名小写集合
  2. @CompoundIndex(def = "{'userid'=1,'likenum'=1}") //指定使用聚合索引的字段
  3. @Data
  4. @AllArgsConstructor
  5. @NoArgsConstructor
  6. @Accessors(chain = true)
  7. public class Comment implements Serializable {
  8. @Id
  9. private String id; //主键,注解意为对应 _id,如果数据的主键字段不为 _id需要添加@Id注解
  10. @Field("content")
  11. private String contents; //评论内容,注解对应mongodb的字段的名字,如果一致,则无需该注解
  12. @Indexed
  13. private String userid; //发布人ID,注解意为使用单字段索引
  14. private Date publishtime; //发布日期
  15. private String nickname; //昵称
  16. private LocalDateTime createdatetime; //评论的日期时间
  17. private Integer likenum; //点赞数
  18. private Integer replynum; //回复数
  19. private String state; //状态
  20. public Comment(String contents, String userid, Date publishtime, String nickname, LocalDateTime createdatetime, Integer likenum, Integer replynum, String state) {
  21. this.contents = contents;
  22. this.userid = userid;
  23. this.publishtime = publishtime;
  24. this.nickname = nickname;
  25. this.createdatetime = createdatetime;
  26. this.likenum = likenum;
  27. this.replynum = replynum;
  28. this.state = state;
  29. }
  30. }

添加配置文件:

用于去除默认保存实体类对象的会创建的 _class 字段

  1. import org.springframework.beans.factory.BeanFactory;
  2. import org.springframework.beans.factory.NoSuchBeanDefinitionException;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.mongodb.MongoDatabaseFactory;
  6. import org.springframework.data.mongodb.core.convert.*;
  7. import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
  8. @Configuration
  9. public class MongoConfig {
  10. @Bean
  11. public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory factory, MongoMappingContext context, BeanFactory beanFactory) {
  12. DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
  13. MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
  14. try {
  15. mappingConverter.setCustomConversions(beanFactory.getBean(MongoCustomConversions.class));
  16. } catch (NoSuchBeanDefinitionException ignore) {
  17. }
  18. mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
  19. return mappingConverter;
  20. }
  21. }

MongoTemplate 案例:

对数据的操作都是基于 MongoTemplate 进行完成,对数据的查询更新需要使用 MongoDB自己的数据类型,操作类似 MybatisPlus

添加数据:

  1. /** MongoTemplate 操作数据案例 */
  2. @RestController
  3. public class HelloController {
  4. @Autowired
  5. private MongoTemplate template;
  6. /*保存*/
  7. @RequestMapping("save")
  8. public void saveComment(){
  9. List<Comment> tempList = new ArrayList<>(3);
  10. tempList.add(new Comment("1","今天天气还行","dmbjz",new Date(),"游客A", LocalDateTime.now(),200,200,"1"));
  11. tempList.add(new Comment("2","手机今天欠费","dmbjz",new Date(),"游客B", LocalDateTime.now(),80,200,"1"));
  12. tempList.add(new Comment("3","你打算在今天干嘛","dmbjz",new Date(),"游客C", LocalDateTime.now(),170,200,"1"));
  13. tempList.add(new Comment("4","我觉得明晚不错","cctv",new Date(),"游客D", LocalDateTime.now(),100,200,"1"));
  14. tempList.add(new Comment("5","要学习知识","acfun",new Date(),"游客E", LocalDateTime.now(),100,200,"1"));
  15. template.insert(tempList,Comment.class); //批量保存
  16. Comment comment = new Comment("额外添加的数据","acfun",new Date(),"游客Z", LocalDateTime.now(),500,30,"1");
  17. template.save(comment); //save只能保存单个数据
  18. }
  19. }

image.png

查询数据:

查询条件需要使用 Query 类进行包装,如果遇到模糊查询需要使用 Pattern 类包装查询条件

  1. @RequestMapping("find")
  2. public void findCommentList(Long page){
  3. //查询全部数据
  4. List<Comment> findList = template.findAll(Comment.class);
  5. findList.forEach(System.out::println);
  6. //根据ID查询
  7. Comment getId = template.findById("1", Comment.class);
  8. System.out.println("根据ID查询到的数据:"+getId);
  9. /*查询以 userid为dmbjz + likenum大于等于100 + content包含“还行”的数据*/
  10. System.out.println("------------开始条件查询------------");
  11. Query query = new Query(
  12. Criteria.where("userid").is("dmbjz")
  13. .and("likenum").gte(100)
  14. );
  15. //使用正则表达式进行匹配,Pattern.CASE_INSENSITIVE :不区分大小写匹配
  16. Pattern pattern = Pattern.compile(".*还行.*", Pattern.CASE_INSENSITIVE);
  17. query.addCriteria(Criteria.where("contents").regex(pattern));
  18. List<Comment> comments = template.find(query, Comment.class);
  19. comments.forEach(System.out::println);
  20. //查询数据总量
  21. long count = template.count(new Query(), Comment.class);
  22. System.out.println("springboot集合中当前文档总数为: " + count);
  23. /*
  24. * 分页查询
  25. * 3 相当于 pageSize
  26. * (page - 1) 相当于当前页码
  27. */
  28. System.out.println("------------开始分页查询------------");
  29. List<Comment> pageList = template.find(new Query().skip((page - 1) * 3).limit(3), Comment.class);
  30. pageList.forEach(System.out::println);
  31. }

image.png

删除数据:

  1. /*删除id不为0的数据*/
  2. @RequestMapping("del")
  3. public void deleteCommentById(String id){
  4. Query query = new Query(Criteria.where("id").nin("0"));
  5. template.remove(query,Comment.class);
  6. }

image.png

修改数据:

  1. /*更新*/
  2. @RequestMapping("update")
  3. public void updateComment(){
  4. //批量更新
  5. Query query = new Query(Criteria.where("userid").is("dmbjz"));
  6. Update update = new Update().set("nickname","修改的名称")
  7. .set("replynum",678);
  8. UpdateResult updateResult = template.updateMulti(query, update, Comment.class);
  9. if(updateResult.getModifiedCount()>0){
  10. System.out.println("批量修改成功!");
  11. }
  12. //单个更新,更新最先查询到的数据
  13. Query query2 = new Query(Criteria.where("userid").is("acfun"));
  14. UpdateResult updateResult2 = template.upsert(query2, update, Comment.class);
  15. if(updateResult2.getModifiedCount()>0){
  16. System.out.println("单独修改成功!");
  17. }
  18. }

image.png

聚合操作:

类似于 MySQL 中的 GroupBy 聚合操作

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

image.png


MongoRepository 案例:

MongoRepository 相较于 MongoTemplate 操作更为简单,需要继承 MongoRepository
每操作一个不同的实体类就需要定义一个 MongoRepository,且模糊查询操作没有 MongoTemplate 灵活便捷

创建 UserRepository:

  1. package com.dmbjz.repository;
  2. import com.dmbjz.entity.Comment;
  3. import org.springframework.data.mongodb.repository.MongoRepository;
  4. import org.springframework.stereotype.Repository;
  5. /**
  6. * 使用 MongoRepository 代替 MongoTemplate 操作数据
  7. * 相较于 MongoTemplate,每操作一个不同的实体类就需要定义一个 MongoRepository
  8. * 参数:操作实体类,Example查询操作类型
  9. */
  10. @Repository
  11. public interface UserRepository extends MongoRepository<Comment, String> {
  12. }

保存数据:

  1. /** MongoRepository 操作数据案例 */
  2. @RestController
  3. @RequestMapping("repository")
  4. public class HelloRepository {
  5. @Autowired
  6. private UserRepository userRepository;
  7. /*保存*/
  8. @RequestMapping("save")
  9. public void saveComment(){
  10. List<Comment> tempList = new ArrayList<>(3);
  11. tempList.add(new Comment("1","今天天气还行","dmbjz",new Date(),"游客A", LocalDateTime.now(),200,200,"1"));
  12. tempList.add(new Comment("2","手机今天欠费","dmbjz",new Date(),"游客B", LocalDateTime.now(),80,200,"1"));
  13. tempList.add(new Comment("3","你打算在今天干嘛","dmbjz",new Date(),"游客C", LocalDateTime.now(),170,200,"1"));
  14. tempList.add(new Comment("4","我觉得明晚不错","cctv",new Date(),"游客D", LocalDateTime.now(),100,200,"1"));
  15. tempList.add(new Comment("5","要学习知识","acfun",new Date(),"游客E", LocalDateTime.now(),100,200,"1"));
  16. userRepository.saveAll(tempList); //批量保存
  17. Comment comment = new Comment("额外添加的数据","acfun",new Date(),"游客Z", LocalDateTime.now(),500,30,"1");
  18. userRepository.save(comment); //保存单个数据
  19. }
  20. }

image.png

查询数据:

  1. /*查询*/
  2. @RequestMapping("find")
  3. public void findCommentList(){
  4. //查询全部数据
  5. List<Comment> findList = userRepository.findAll();
  6. findList.forEach(System.out::println);
  7. //根据ID查询
  8. Optional<Comment> info = userRepository.findById("1");
  9. info.ifPresent(a->
  10. System.out.println("根据ID查询到的数据:"+ a.getId())
  11. );
  12. /*
  13. * 精确查询
  14. * 查询所有结果为 userid为dmbjz + likenum等于100 的数据
  15. */
  16. Comment comment = new Comment();
  17. comment.setUserid("dmbjz");
  18. comment.setLikenum(100);
  19. Example<Comment> userExample = Example.of(comment);
  20. List<Comment> comments = userRepository.findAll(userExample);
  21. comments.forEach(System.out::println);
  22. /*
  23. * 模糊查询
  24. * 缺点:只能对所有条件字段统一进行某种特定的搜索,但数值类型只能进行精确匹配
  25. * 例:查询 userid包含dmbjz + likenum等于20 + content包含“还行”的数据
  26. */
  27. System.out.println("========== 模糊搜索案例 START ============");
  28. ExampleMatcher matcher = ExampleMatcher.matching() //构建匹配器
  29. .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
  30. .withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
  31. Comment comment2 = new Comment();
  32. comment2.setUserid("dmbjz");
  33. comment2.setLikenum(20); //数值类型只能精确查询
  34. comment2.setContents("还行");
  35. Example<Comment> userExample2 = Example.of(comment2, matcher);
  36. List<Comment> userList = userRepository.findAll(userExample2);
  37. System.out.println(userList);
  38. System.out.println("========== 模糊搜索案例 END ============");
  39. //查询数据总量
  40. long count = userRepository.count();
  41. System.out.println("springboot集合中当前文档总数为: " + count);
  42. /*
  43. * 分页查询
  44. */
  45. System.out.println("========== 分页查询 START ============");
  46. Sort sort = Sort.by(Sort.Direction.DESC, "age"); //根据Age字段倒序
  47. Pageable pageable = PageRequest.of(0, 10, sort); //创建分页对象,参数: 当前页索引(第1页index 0,第二页index 1....)、数据显示数、排序
  48. ExampleMatcher pageMatcher = ExampleMatcher.matching() //构建匹配器
  49. .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
  50. .withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
  51. Comment temp = new Comment();
  52. temp.setNickname("游客A");
  53. Example<Comment> pageExample = Example.of(temp, pageMatcher); //创建实例
  54. Page<Comment> pages = userRepository.findAll(pageExample, pageable); //分页查询
  55. System.out.println("总页数:"+pages.getTotalPages());
  56. System.out.println("数据列表:"+pages.getContent());
  57. System.out.println("是否有下一页:"+pages.hasNext());
  58. System.out.println("========== 分页查询 END ============");
  59. }

image.png

删除数据:

无论是 delete 方法还是 deletAll 方法,其内部参数都需要携带主键ID字段,否则会在 getByIdQuery() 中抛出异常,因此使用 deleteById 方法即可

  1. /*删除*/
  2. @RequestMapping("del")
  3. public void deleteCommentById(){
  4. userRepository.deleteById("3"); //删除 id = 3 的数据
  5. }

image.png

更新数据:

需要先查询再更新数据,如果没有先查询将会进行覆盖更新
更新操作依赖主键ID字段

  1. /*更新*/
  2. @RequestMapping("update")
  3. public void updateComment(){
  4. //单个更新,先查询再更新数据,需要依赖主键ID字段
  5. Optional<Comment> findComment = userRepository.findById("4");
  6. findComment.ifPresent(info->{
  7. info.setNickname("dmbjz_new");
  8. info.setContents("MongoRepository更新操作");
  9. info.setLikenum(999);
  10. userRepository.save(info);
  11. });
  12. //批量更新,需要依赖主键ID字段
  13. List<Comment> upList = new ArrayList<>(2);
  14. Comment temp = new Comment().setId("1").setContents("今天天气不行");
  15. Comment temp2 = new Comment().setId("2").setContents("今天手机交话费了");
  16. upList.add(temp);
  17. upList.add(temp2);
  18. userRepository.saveAll(upList);
  19. }

image.png

SpringData 快捷查询支持:

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

修改 UserRepository:

  1. @Repository
  2. public interface UserRepository extends MongoRepository<Comment, String> {
  3. //SpringData规范查询数据方法一
  4. Comment findByContentsAndLikenum(String contents,Integer likenum);
  5. //SpringData规范查询数据方法二
  6. List<Comment> readAllByUserid(String nickname);
  7. }

查询测试:

  1. /* SpringData规范查询数据 */
  2. @RequestMapping("data")
  3. public void springData(){
  4. Comment info = userRepository.findByContentsAndLikenum("要学习知识", 100);
  5. System.out.println(info);
  6. List<Comment> infoList = userRepository.readAllByUserid("dmbjz");
  7. infoList.forEach(System.out::println);
  8. }

image.png