前置文章:
MongoDB-基础使用(一),该文主要介绍MongoDB概念、安装、库/集合(表)/文档(行)操作。
零、本文纲要
一、索引
- 索引类型
- 索引管理操作
- 索引使用
二、API操作-快速入门
- pom.xml
- application.yml
- 启动类
- 测试
- 创建测试集合实体类
- 持久层接口&业务层编写
- 测试
- 根据上级ID查询分页列表
- MongoTemplate
一、索引
MongoDB索引使用B树数据结构(确切的说是B-Tree,MySQL是B+Tree)
注意:该部分的内容与Redis、JS类似,整体特性又与MySQL相近。可以简单了解以下。
1. 索引类型
a、单字段索引
b、复合索引
c、地理空间索引
d、文本索引
e、哈希索引
2. 索引管理操作
① 索引查看
# 索引查看
db.collection.getIndexes()
#如:
db.collection_test.getIndexes()
> db.collection_test.getIndexes()
[
{
"v" : 2, #索引的版本号
"key" : { #索引的字段
"_id" : 1 #1代表升序
},
"name" : "_id_", #索引的名称,默认为 "字段名_" + "升降序规则数字",如:"userid_1_nickname_-1"
"ns" : "db_test.collection_test" #命名空间,数据库.集合
}
]
② 索引创建
# 创建单字段索引
db.collection.createIndex(keys, options)
#如:
db.collection_test.createIndex({userid: 1}) #此处1代表升序,-1则为降序,单字段索引升降序不影响整体查询效率
# 创建复合索引
#如:
db.collection_test.createIndex({userid: 1, nickname: -1})
以下了解即可:
参数 | 类型 | 描述 |
---|---|---|
background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false。 |
unique | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为false. |
name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。 |
dropDups | Boolean | 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false. |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false. |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
weights | document | 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。 |
default_language | string | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语 |
language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language. |
③ 索引移除
# 索引移除
db.collection.dropIndex(index)
#如:
db.collection_test.dropIndex("userid_1")
db.collection_test.dropIndex({userid: 1})
# 移除所有索引(默认"_id_"索引不会被移除)
db.collection_test.dropIndexes()
3. 索引使用
① 执行计划
# 执行计划
db.collection.find(query,options).explain(options)
#如:
db.collection_test.find({userid: "1003"}).explain()
"stage" : "COLLSCAN", 表示全集合扫描
"stage" : "IXSCAN" ,基于索引的扫描
② 涵盖查询(索引覆盖)
db.collection_test.find(
{userid: "1003"},
{userid:1,_id:-1}
).explain()
二、API操作-快速入门
注意:该部分内容与JPA、MyBatis类似,简单了解即可。使用的时候能查询资料,能看懂就可以。
1. pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
com.stone
mongodb-springboot
1.0-SNAPSHOT
8
8
1.18.22
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-data-mongodb
org.projectlombok
lombok
${lombok.version}
2. application.yml
spring:
#数据源配置
data:
mongodb:
# 主机地址
host: 192.168.253.128
# 数据库
database: test_db
# 默认端口是27017
port: 27017
#也可以使用uri连接
#uri: mongodb://192.168.253.128:27017/test_db
3. 启动类
@SpringBootApplication
public class MongodbApplication {
public static void main(String[] args) {
SpringApplication.run(MongodbApplication.class, args);
}
}
4. 测试
此时,只要能正常连接,不报错即可。
5. 创建测试集合实体类
① @Document注解
类名小写与集合名一致则可以省略属性配置,如果省略,则默认使用类名小写映射集合;
假设集合名为"comment",则直接使用@Document即可。
② @CompoundIndex注解
@CompoundIndex(def = "{'userid': 1, 'nickname': -1}"),用于指定复合索引;
还是推荐在client通过指令提前生成索引,不推荐使用注解生成索引。
③ @Id注解
主键标识,该属性的值会自动对应mongodb的主键字段"_id";
如果该属性名就叫“id”,则该注解可以省略,否则必须写。
④ @Field注解
该属性对应mongodb的字段的名字,如果一致,则无需该注解。
⑤ @Indexed注解
添加了一个单字段的索引;
还是推荐在client通过指令提前生成索引,不推荐使用注解生成索引。
/**
* 文章评论实体类
*/
//把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。
//@Document(collection="mongodb 对应 collection 名")
// 若未加 @Document ,该 bean save 到 mongo 的 comment collection
// 若添加 @Document ,则 save 到 comment collection
@Document(collection="collection_test")//类名小写与集合名一致则可以省略,如果省略,则默认使用类名小写映射集合
//复合索引
// @CompoundIndex( def = "{'userid': 1, 'nickname': -1}")
@Data //lombok的注解
public class Comment implements Serializable {
//主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写
@Id //此处其实可以省略
private String id;//主键
//该属性对应mongodb的字段的名字,如果一致,则无需该注解
@Field("content") //此处该注解其实可以省略
private String content;//吐槽内容
private Date publishtime;//发布日期
//添加了一个单字段的索引
@Indexed
private String userid;//发布人ID
private String nickname;//昵称
private LocalDateTime createdatetime;//评论的日期时间
private Integer likenum;//点赞数
private Integer replynum;//回复数
private String state;//状态
private String parentid;//上级ID
private String articleid;
}
6. 持久层接口&业务层编写
① 持久层接口
该接口需要继承MongoRepository接口,并指定实体类、主键类型,如下:
public interface CommentRepository extends MongoRepository {
}
MongoRepository接口,如下:
@NoRepositoryBean
public interface MongoRepository extends PagingAndSortingRepository, QueryByExampleExecutor {
@Override
List saveAll(Iterable entities);
@Override
List findAll();
@Override
List findAll(Sort sort);
S insert(S entity);
List insert(Iterable entities);
@Override
List findAll(Example example);
@Override
List findAll(Example example, Sort sort);
可以看到基础的插入、查询操作都有,运行时则会生成其代理对象执行具体方法。
② 业务层
public interface CommentService {
public void saveComment(Comment comment);
public void updateComment(Comment comment);
public void deleteCommentById(String id);
public List findCommentList();
public Comment findCommentById(String id);
}
/**
* 评论的业务层
*/
@Service
public class CommentServiceImpl implements CommentService {
//注入dao
@Autowired
private CommentRepository commentRepository;
/**
* 保存一个评论
* @param comment
*/
public void saveComment(Comment comment){
//如果需要自定义主键,可以在这里指定主键;如果不指定主键,MongoDB会自动生成主键
//设置一些默认初始值。。。
//调用dao
commentRepository.save(comment);
}
/**
* 更新评论
* @param comment
*/
public void updateComment(Comment comment){
//调用dao
commentRepository.save(comment);
}
/**
* 根据id删除评论
* @param id
*/
public void deleteCommentById(String id){
//调用dao
commentRepository.deleteById(id);
}
/**
* 查询所有评论
* @return
*/
public List findCommentList(){
//调用dao
return commentRepository.findAll();
}
/**
* 根据id查询评论
* @param id
* @return
*/
public Comment findCommentById(String id){
//调用dao
return commentRepository.findById(id).get();
}
}
7. 测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MongodbApplication.class)
public class CommentServiceImplTest {
//注入Service
@Autowired
private CommentService commentService;
/**
* 保存一个评论
*/
@Test
public void testSaveComment(){
Comment comment=new Comment();
comment.setArticleid("100000");
comment.setContent("测试添加的数据");
comment.setCreatedatetime(LocalDateTime.now());
comment.setUserid("1003");
comment.setNickname("凯撒大帝");
comment.setState("1");
comment.setLikenum(0);
comment.setReplynum(0);
commentService.saveComment(comment);
}
/**
* 查询所有数据
*/
@Test
public void testFindAll(){
List list = commentService.findCommentList();
System.out.println(list);
}
/**
* 测试根据id查询
*/
@Test
public void testFindCommentById(){
Comment comment = commentService.findCommentById("62976eb31629ea868c6b5511");
System.out.println(comment);
}
}
8. 根据上级ID查询分页列表
这个地方非常特殊,必须要在实体类内设置parentid属性,然后跟据该属性进行分页。
① CommentRepository新增方法定义
//根据父id,查询子评论的分页列表
Page findByParentid(String parentid, Pageable pageable);
② CommentService新增方法
public Page findCommentListPageByParentid(String parentid, int page, int size);
/**
* 根据父id查询分页列表
* @param parentid
* @param page
* @param size
* @return
*/
public Page findCommentListPageByParentid(String parentid, int page, int size){
return commentRepository.findByParentid(parentid, PageRequest.of(page - 1, size));
}
③ 测试
注意:一定要先插入带有parentid属性的数据再测试
/**
* 测试根据父id查询子评论的分页列表
*/
@Test
public void testFindCommentListPageByParentid(){
Page pageResponse = commentService.findCommentListPageByParentid("3", 1, 2);
System.out.println("----总记录数:"+pageResponse.getTotalElements());
System.out.println("----当前页数据:"+pageResponse.getContent());
}
9. MongoTemplate
继承MongoRepository接口默认只能实现基础的查询、插入、保存操作,而且较为复杂的操作需要先查询、再设置新数据、最后保存,效率较低。
/**
* 点赞-效率低
* @param id
*/
public void updateCommentThumbupToIncrementingOld(String id){
Comment comment = CommentRepository.findById(id).get();
comment.setLikenum(comment.getLikenum() + 1);
CommentRepository.save(comment);
}
使用MongoTemplate类可以提升此效率,用法如下:
public void updateCommentLikenum(String id);
//注入MongoTemplate
@Autowired
private MongoTemplate mongoTemplate;
/**
* 点赞数+1
*
* @param id
*/
@Override
public void updateCommentLikenum(String id) {
//查询对象
Query query = Query.query(Criteria.where("_id").is(id));
//更新对象
Update update = new Update();
//局部更新,相当于$set
// update.set(key,value)
//递增$inc
// update.inc("likenum",1);
update.inc("likenum");
//参数1:查询对象
//参数2:更新对象
//参数3:集合的名字或实体类的类型Comment.class
mongoTemplate.updateFirst(query, update, "collection_test");
}
测试:
/**
* 点赞数+1
*/
@Test
public void testUpdateCommentLikenum(){
//对3号文档的点赞数+1
commentService.updateCommentLikenum("62976eb31629ea868c6b5511");
}
三、结尾
以上即为MongoDB-基础使用(二)的全部内容,感谢阅读。