介绍 MongoTemplate 与 MongoRepository 对 MongoDB 数据的操作
码云地址:
https://gitee.com/dmbjzorg/Springboot_Mongdb
配置:
创建SpringBoot项目,勾选 MongoDB依赖进行搭建
勾选必要的依赖
填写配置文件:
spring.data.mongodb.host=192.168.182.129
spring.data.mongodb.port=27017
spring.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 {
@Id
private String id; //主键,注解意为对应 _id,如果数据的主键字段不为 _id需要添加@Id注解
@Field("content")
private String contents; //评论内容,注解对应mongodb的字段的名字,如果一致,则无需该注解
@Indexed
private 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;
@Configuration
public class MongoConfig {
@Bean
public 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 操作数据案例 */
@RestController
public class HelloController {
@Autowired
private 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 > 80
Aggregation.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 ,分页结束
);
//聚合查询,参数一:聚合条件,参数二:查询数据对应实体类,参数三:返回结果Class
AggregationResults<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查询操作类型
*/
@Repository
public interface UserRepository extends MongoRepository<Comment, String> {
}
保存数据:
/** MongoRepository 操作数据案例 */
@RestController
@RequestMapping("repository")
public class HelloRepository {
@Autowired
private 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:
@Repository
public 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);
}