目录
- 前言
- 1、表及字段设计
- 2、引入pom
- 3、yml配置
- 4、实体类创建
- 5、Repository
- 5.1、基础实现
- 5.2、方法查询
- 5.3、语句查询
- 6、具体实现
- 0、创建Repository
- 1、新增数据
- 2、修改数据
- 3、删除数据
- 4、根据ID查询
- 5、聚合查询
- 6、列表分页+条件+排序查询
前言
使用SpringBoot+MongoDB整合实现文章评论
的实例操作。
1、表及字段设计
数据库名称:articledb
名称 | 含义 | 类型 |
_id | 主键 | ObjectId或String |
articleId | 文章ID | String |
content | 评论内容 | String |
userId | 评论人ID | String |
nickname | 评论人名称 | String |
createTime | 创建时间 | Date |
likeNum | 点赞数量 | Int32 |
replyNum | 回复数量 | Int32 |
state | 状态(0:不可见;1:可见;) | String |
parentId | 父级评论ID(0为顶级) | String |
2、引入pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
3、yml配置
spring:
#数据源配置
data:
mongodb:
# 单机,root与123456分别为账号和密码
uri: mongodb://127.0.0.1:27017/articledb
# 集群连接,testSet为副本集名称,如果开启验证mongodb://user:pwd@111....
# uri: mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=testSet
4、实体类创建
@Document(collection = "comment")
@Data
public class Comment implements Serializable {
/**
* 主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写
*/
@Id
private String id;
/**
* 文章ID
*/
private String articleId;
/**
* 该属性对应mongodb的字段的名字,如果一致,则无需该注解
*/
@Field("content")
private String content;
/**
* 发布人ID,并且设置为单字段索引
*/
private String userId;
/**
* 昵称
*/
private String nickName;
/**
* 评论的日期时间
*/
private LocalDateTime createTime;
/**
* 点赞数
*/
private Integer likeNum;
/**
* 回复数
*/
private Integer replyNum;
/**
* 状态(0:不可见;1:可见;)
*/
private String state;
/**
* 上级ID
*/
private String parentId;
}
5、Repository
5.1、基础实现
通过实现MongoRepository<T, ID>
接口,创建Repository
类,其中T
表示实体类的class,ID
表示主键的数据类型,比如:
public interface CommentRepository extends MongoRepository<Comment, String> {}
在实现MongoRepository<T, ID>
接口后,会默认实现以下接口:
<S extends T> List<S> saveAll(Iterable<S> var1);
List<T> findAll();
List<T> findAll(Sort var1);
<S extends T> S insert(S var1);
<S extends T> List<S> insert(Iterable<S> var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
Optional<T> findById(ID var1);
boolean existsById(ID var1);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable<? extends T> var1);
void deleteAll();
5.2、方法查询
除此之外也可以根据具体的方法名称进行查询相应内容,比如:findByName(String name)
、findByNameAndUserId(String name,String userId)
5.3、语句查询
通过@Query
编写类似sql的查询语句,支持动态传入参数,比如:
@Query(value="{\"username\":{\"$regex\":?0},\"age\":{\"$gte\":?1,\"$lte\": ?2}}")
Page<User> findByNameAndAgeRange(String name,int age,int ageTo,Pageable page);
6、具体实现
0、创建Repository
public interface CommentRepository extends MongoRepository<Comment, String> {
}
1、新增数据
@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
@Resource
private CommentRepository commentRepository;
@Resource
private MongoTemplate mongoTemplate;
@PostMapping
public void saveComment(@RequestBody Comment comment) {
int id = (int) (Math.random() * 10000000);
int userId = (int) (Math.random() * 10);
int articleId = (int) (Math.random() * 10);
comment.setId(id + "");
comment.setReplyNum(articleId);
comment.setLikeNum(userId);
comment.setArticleId(articleId + "");
comment.setUserId(userId + "");
comment.setNickName("测试人员");
comment.setParentId("0");
comment.setState("1");
comment.setContent("测试内容");
comment.setCreateTime(System.currentTimeMillis());
commentRepository.save(comment);
// 如果批量新增
List<Comment> list = new ArrayList<>();
for (int i = 0; i <= 2; i++) {
Comment comment2 = new Comment();
BeanUtils.copyProperties(comment, comment2);
int newId = (int) (Math.random() * 100000);
int newUserId = (int) (Math.random() * 10);
int newArticleId = (int) (Math.random() * 10);
comment2.setId(newId + "");
comment2.setReplyNum(articleId);
comment2.setLikeNum(userId);
comment2.setArticleId(newArticleId + "");
comment2.setUserId(newUserId + "");
comment.setNickName("测试人员");
comment.setParentId("0");
comment.setState("1");
comment.setContent("测试内容");
comment2.setCreateTime(System.currentTimeMillis());
list.add(comment2);
}
commentRepository.saveAll(list);
}
}
2、修改数据
@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
@Resource
private CommentRepository commentRepository;
@Resource
private MongoTemplate mongoTemplate;
@PutMapping
public void updateComment(@RequestBody Comment comment) {
comment.setId("1847892");
comment.setNickName("测试人员-修改");
comment.setParentId("0");
comment.setState("1");
comment.setContent("测试内容");
comment.setCreateTime(System.currentTimeMillis());
// 使用save时,如果数据存在则更新,不存在则新增
commentRepository.save(comment);
}
}
3、删除数据
@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
@Resource
private CommentRepository commentRepository;
@Resource
private MongoTemplate mongoTemplate;
// 根据Id删除
@DeleteMapping("/{id}")
public void deleteCommentById(@PathVariable("id") String id) {
commentRepository.deleteById(id);
}
// 按条件删除
@DeleteMapping("/condition")
public void deleteCommentByArticleId() {
// 删除articleId为10的数据
Query query = new Query(Criteria.where("articleId").is("10"));
mongoTemplate.remove(query, Comment.class);
}
}
4、根据ID查询
@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
@Resource
private CommentRepository commentRepository;
@Resource
private MongoTemplate mongoTemplate;
@GetMapping("/{id}")
public Object findCommentById(@PathVariable("id") String id) {
/**
* mongoTemplate实现
* Query query = new Query(Criteria.where("_id").is(id));
* mongoTemplate.remove(query, Comment.class);
*/
return commentRepository.findById(id);
}
}
5、聚合查询
@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
@Resource
private CommentRepository commentRepository;
@Resource
private MongoTemplate mongoTemplate;
@GetMapping("/agg")
public ResponseObject agg() {
// 1、单/多字段分组 - 分页排序
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("userId", "articleId").count().as("userNum"),
Aggregation.skip(1L),
Aggregation.limit(10),
Aggregation.sort(Sort.Direction.DESC, "userNum")
);
AggregationResults<BasicDBObject> basicDBObjects = mongoTemplate.aggregate(aggregation, Comment.class, BasicDBObject.class);
System.out.println("basicDBObjects:");
for (DBObject obj : basicDBObjects) {
System.out.println(obj);
}
// 2、条件匹配分组,$match表示匹配条件,在group前表示where,在后表示having,userid为分组字段
// select userid, count(*) as num from comment where articleId in(4,5) likeNum >= 1 and likeNum =<10 group by userid having num >=2
Aggregation aggregation2 = Aggregation.newAggregation(
Aggregation.match(Criteria.where("likeNum").gte(1).lte(10).and("articleId").in("4", "5")),
Aggregation.group("userId").count().as("userNum"),
Aggregation.match(Criteria.where("userNum").gte(2))
);
AggregationResults<BasicDBObject> basicDBObjects2 = mongoTemplate.aggregate(aggregation2, Comment.class, BasicDBObject.class);
System.out.println("basicDBObjects2:");
for (DBObject obj : basicDBObjects2) {
System.out.println(obj);
}
// 3、聚合-求某个数值平均值、最大值、最小值
Aggregation aggregation3 = Aggregation.newAggregation(
Aggregation.match(Criteria.where("likeNum").gte(1).lte(10)),
Aggregation.group("userId").sum("likeNum").as("likeNumSum")
.avg("likeNum").as("likeNumAvg")
// Aggregation.match(Criteria.where("userNum").gte(2))
);
AggregationResults<BasicDBObject> basicDBObjects3 = mongoTemplate.aggregate(aggregation3, Comment.class, BasicDBObject.class);
System.out.println("basicDBObjects3:");
for (DBObject obj : basicDBObjects3) {
System.out.println(obj);
}
// 4、聚合分组数组 ,等同于group_concat,push-元素可重复,addToSet-元素不可重复
Aggregation aggregation4 = Aggregation.newAggregation(
Aggregation.group("userId")
.addToSet("articleId").as("setList")
.push("articleId").as("pushList")
);
AggregationResults<BasicDBObject> basicDBObjects4 = mongoTemplate.aggregate(aggregation4, Comment.class, BasicDBObject.class);
System.out.println("basicDBObjects4:");
for (DBObject obj : basicDBObjects4) {
System.out.println(obj);
}
}
}
6、列表分页+条件+排序查询
为了实现统一的分页查询,封装分页工具类:PageBuilder
、NewPageBean
,代码如下:
PageBuilder:
import org.springframework.data.mongodb.core.query.Query;
public class PageBuilder {
/**
* @param query 查询
* @param pageSize 当前页数
* @param pageNum 当前页码
*/
public static void startPage(Query query, Integer pageSize, Integer pageNum) {
if (pageSize == null || pageNum == null) {
pageSize = 10;
pageNum = 1;
}
int offset = (pageNum - 1) * pageSize;
query.skip(offset).limit(pageSize);
}
}
NewPageBean:
import com.lhz.search.model.param.PageParam;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import java.util.List;
public class NewPageBean<T> {
private Long total;
private List<T> list;
private MyPage pageInfo;
public NewPageBean(List<T> list, Class<?> entityClass, PageParam param, MongoTemplate mongoTemplate) {
Integer pageNum = param.getPageNum();
Integer pageSize = param.getPageSize();
long totalSize = mongoTemplate.count(new Query(), entityClass);
//计算总页数
long totalPage = totalSize % pageSize == 0 ? totalSize / pageSize : totalSize / pageSize + 1;
// 数据组装
this.list = list;
this.total = totalSize;
MyPage page = new MyPage();
page.setTotalPage(totalPage);
page.setPageNum(pageNum);
page.setPageSize(pageSize);
page.setPreviousPage(pageNum - 1);
page.setNextPage(pageNum + 1);
this.pageInfo = page;
}
public long getNum() {
return this.total;
}
public void setNum(Long total) {
this.total = total;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public MyPage getPageInfo() {
return pageInfo;
}
public void setPageInfo(MyPage pageInfo) {
this.pageInfo = pageInfo;
}
class MyPage {
private int pageNum;
private int pageSize;
private long totalPage;
private int nextPage;
private int previousPage;
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public long getTotalPage() {
return totalPage;
}
public void setTotalPage(long totalPage) {
this.totalPage = totalPage;
}
public int getNextPage() {
return nextPage;
}
public void setNextPage(int nextPage) {
this.nextPage = nextPage;
}
public int getPreviousPage() {
return previousPage;
}
public void setPreviousPage(int previousPage) {
this.previousPage = previousPage;
}
}
}
查询参数-CommentParam:
@Data
public class CommentParam extends PageParam implements Serializable {
/**
* 文章ID
*/
private String articleId;
/**
* 用户Id
*/
private String userId;
/**
* 关键字
*/
private String keyWord;
/**
* 昵称(模糊查询)
*/
private String nickName;
/**
* 开始时间
*/
private Long startTime;
/**
* 结束时间
*/
private Long endTime;
}
Controller:
@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
@Resource
private CommentRepository commentRepository;
@Resource
private MongoTemplate mongoTemplate;
@GetMapping
public Object findCommentList(CommentParam param) {
// 如果只是单一条件
// Query query = new Query(Criteria.where("nickName").is("查询内容"));
// List<Comment> comment = mongoTemplate.find(query, Comment.class);
// Comment one = mongoTemplate.findOne(query, Comment.class);
// 多条件匹配,复杂查询
Criteria criteria = new Criteria();
if (param.getArticleId() != null) {
criteria.and("articleId").is(param.getArticleId());
}
if (param.getUserId() != null) {
criteria.and("userId").is(param.getUserId());
}
if (param.getNickName() != null) {
//模糊匹配
//右匹配
// Pattern pattern = Pattern.compile("^.*"+param.getKeyWord()+"$", Pattern.CASE_INSENSITIVE);
//左匹配
// Pattern pattern = Pattern.compile("^"+param.getKeyWord()+".*$", Pattern.CASE_INSENSITIVE);
Pattern pattern = Pattern.compile("^.*" + param.getNickName() + ".*$", Pattern.CASE_INSENSITIVE);
criteria.and("nickName").regex(pattern);
}
if (param.getKeyWord() != null) {
//模糊匹配 - 或查询
Pattern pattern = Pattern.compile("^.*" + param.getKeyWord() + ".*$", Pattern.CASE_INSENSITIVE);
criteria.orOperator(Criteria.where("nickName").regex(pattern), Criteria.where("content").regex(pattern));
}
if (param.getEndTime() != null && param.getStartTime() != null) {
criteria.andOperator(Criteria.where("createTime").gte(param.getStartTime()), Criteria.where("createTime").lte(param.getEndTime()));
}
Query query = new Query();
// 添加查询条件
query.addCriteria(criteria);
// 排序-时间降序,userId生效
query.with(Sort.by(Sort.Order.desc("createTime"), Sort.Order.asc("userId")));
// 分页
PageBuilder.startPage(query, param.getPageSize(), param.getPageNum());
// 查询
List<Comment> list = mongoTemplate.find(query, Comment.class);
// 分页参数封装
return new NewPageBean<>(list, Comment.class, param, mongoTemplate);
}
}