快速入门MongoDB

上手MongoDB

MongoDB 是一个跨平台的,面向文档的数据库,如果你了解spring-data-jpa的使用, 那么恭喜你,你已经可以使用mongodb做开发了

使用这种类型的数据库还是挺方便的,最小的存储单位是一个文档,但是文档有什么字段,有多少字段它都不关心,而mysql这样的典型的关系型数据库,开发之前得把表设计的明明白白的,说不定还得预留几个字段以备不时之需,因为后续再改就麻烦了

。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

体系结构

MongoDB Mysql
database database
collection 数据表
document 表中的一行记录

一个MongoDB实例支持多个database并存,同时一个database中可以包含多个collection,所以大家都说它是介于关系数据库和非关系数据库之间,因为它的组成结构真的特别像关系型数据库

支持的数据类型

数据类型名 BSON
null {"XXX":null}
布尔值: {"XXX":true/false}
int {"XXX":NumberInt("1")}
Long {"XXX":NumberLong("1")}
字符串 {"XXX":"啊哈哈哈"}
日期 {"XXX":new Date()}
正则 {"XXX":null}
布尔值: {"XXX":/[abc]/}
数组 {"XXX":["a","b","c"]}
文档的嵌套: {"XXX":{"YYY":3}}
对象id {"XXX":objectId()}

注意点:

  • 当我们再在命令行窗口通过命令行进行数据的读写时,如果我们这样写 {"XXX",2}, 它会默认把2处理成浮点类型,而不是整形
  • 每一个文档唯一身份id是 _id , 前面有个下划线

使用命令行进行数据的读写

点击文档官网

  • 开启本地的mongo服务器(用来保存数据)打开之后,就别关了

默认使用的端口是 27017

1
mongod -dbpath=指定文件夹当作数据存储路径 --port 端口号
  • 连接数据库(客户端,进行CRUD)
1
2
3
4
// 在本机直接用下面的命令,模拟连接本机的mongodb
mongo
// 连接远程的mongo
mongo 192.168.88.88
  • 退出
1
exit

  • 选择数据库

默认选中的数据库是test库

1
2
// 如果数据名不存在,默认直接把它创建出来,并且直接选中它使用
user 数据库名
  • 查看当前数据库
1
db
  • 保存一条数据
1
2
入参位置的bson, 注意key的部分是没有双引号的
db.集合名.insert({content:"哈哈,保存了数据"})
  • 查询全部数据, 默认按照_id从小到大排序
1
db.集合名.find()
  • 返回满足条件的全部数据
1
2
入参部分的查询条件 bson同样是json格式
db.集合名.find({id:"123"})
  • 返回满足条件的第一条数据
1
2
入参部分的查询条件 bson同样是json格式
db.集合名.findOne({id:"123"})
  • 根据限制的数量, 返回满足条件的数据
1
2
入参部分的查询条件 bson同样是json格式
db.集合名.find({id:"123"}).limit(1)
  • 更新文档
1
2
3
4
5
6
7
db.集合名.update(条件,修改后的数据)
 
// 下面的这条修改语句存在问题,只会保留name字段
db.集合.update({_id:"1"},{name:"新名字"})
 
// 保留其他字段的写法 db.集合名.update({条件},{$set:{新的值} })
db.集合.update({_id:"1"},{$set:{name:"新名字"}})
  • 删除文档
1
2
3
4
db.集合.remove(条件)
 
// 删除全部
db.集合.remove({})
  • 统计数量
1
2
3
db.spit.count()
 
db.spit.count({条件})
  • 使用正则实现.模糊查询
    /将被模糊查询的字符串/
1
2
3
4
db.集合名.find({content:/将被模糊查询的字符串/})
 
//匹配以XXX开头的文档
db.集合名.find({content:/^XXX/})
  • 大于,小于,不等于
1
2
3
4
5
db.集合名.find({"参考的字段":{$gt:value}}) // 查找参考的字段大于value值的文档
$lt 小于
$lte 小于等于   
$gte 大于等于
$ne 不等于
  • 包含和不包含
1
2
db.集合名.find({_id:{$in:["1","2","3"]}})
db.集合名.find({_id:{$nin:["1","2","3"]}})
  • 条件连接
1
2
3
4
5
$and:[{},{},{}]
// 查询id 大于1000,小于2000的文档
db.集合名.find({$and:[{_id:{$gte:1000}},{_id:{$lte:2000}}]})
 
$or:[{},{},{}]
  • 实现列值的增长
1
db.集合名.update({_id:"12"},{$inc:{number:NumberInt(1)}})
  • 按照某个字段排序
1
2
3
4
5
6
db.集合名.find({}).sort(字段:1) // 1是升序
 
db.集合名.find({}).sort(字段:-1) // -1是降序
 
// 先按照字段1升序, 如果字段1的值相同,按照字段2降序排序
db.集合名.find({}).sort(字段:1| 字段2:-1)

Java操作MongoDB

添加坐标

1
2
3
4
5
 <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongodb-driver</artifactId>
      <version>3.6.3</version>
  </dependency>

配置文件

1
2
3
4
5
6
server:
  port: 9002
  data:
    mongodb:
      host: 192.168.88.130
      database: 数据库名字

使用mongo官方提供的java的驱动包,相当于jdbc

1
2
3
4
5
6
7
8
9
10
11
// 连接mongo服务器
MongoClient client = new MongoClient("192.168.88.130");
// 得到想操作的数据库
MongoDatabase db = client.getDatabase("数据库名");
// 得到想要操作的集合
MongoCollection<Document> collection = db.getCollection("集合名");
 
// todo CRUD with collection
 
// 释放资源
client.close();

查询全部

1
2
3
4
5
FindIterable<Document> documents = collection.find();
 
for (Document document : documents) {
        System.out.println(document);
    }

根据限制条件查询

  • 查询出content字段为"我很开心"全部文档
1
2
3
4
// 条件查询限制为  userid==123
BasicDBObject bson = new BasicDBObject("content", "我很开心");
 
FindIterable<Document> documents = collection.find(bson);
  • 封装条件,查询访问量大于1000的,可以还可以更换成gt gte等等
1
2
3
4
//    find(visit:{$gt:1000})
BasicDBObject bson = new BasicDBObject("visits", new BasicDBObject("$gt",1000));
 
FindIterable<Document> documents = collection.find(bson);

我发现如果是使用_id当作限制条件,此时如果_id的类型是原生的objectid类型的话,是查询不处任何结果的,但是如果换成我们自定义的_id, 就可以当成限制条件匹配出相应的文档

添加一个文档

_id不能重复

1
2
3
4
5
6
HashMap<String, Object> map = new HashMap<>();
map.put("_id","10");
map.put("content","啊");
map.put("article","嘻嘻嘻嘻嘻");
Document document = new Document(map);
collection.insertOne(document);

添加嵌套文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Document document = new Document();
 
// 插入普通字段
document.put("_id","123");
document.put("key1","value1");
document.put("key2","value2");
document.put("key3","value3");
 
Document doc = new Document();
doc.put("key4","value4");
// 嵌套文档
document.put("docDetail",doc);
 
collection.insertOne(document)

更新

  • 更新数值类型的数,在原来的基础上增加
1
2
3
4
5
// 使用$inc ,在将入参2位置对应的文档的指定字段增加指定的数
BasicDBObject newDoc = new  BasicDBObject().append("$inc",new BasicDBObject().append("thumbup",1));
 
// 第一个参数是限制条件找到要被更新的bson, 第二个参数是将被改变的值
collection.updateOne(new  BasicDBObject().append("content","我是新的content"),newDoc);
  • 更新普通字符串类型
1
2
3
4
5
// 使用$set ,在将入参2位置对应的文档的指定字段更新为指定的值
BasicDBObject newDoc = new  BasicDBObject().append("$set",new BasicDBObject().append("nickname","朱昌武"));
 
// 第一个参数是限制条件找到要被更新的bson, 第二个参数是将被改变的值
collection.updateOne(new  BasicDBObject().append("_id","1"),newDoc);

替换文档

同样,如果入参1位置的过滤条件,使用的_id是原生ObjectId类型的话,匹配不出任何结果

1
2
3
4
5
6
7
8
9
BasicDBObject bson = new BasicDBObject("_id", "5d4fd7cb92cb2acaf109a46e");
 
//   Document filter = new Document();
//   filter.append("_id", "5d4fd7cb92cb2acaf109a46e");
 
Document replacement = new Document();
replacement.append("key1","我替换了一第一个文档");
 
collection.replaceOne(bson,replacement);

删除文档

1
2
3
4
5
6
7
8
9
10
11
// 删除一个
Document filter = new Document();
filter.append("hello", "world");
 
collection.deleteOne(filter);
 
// 删除多个
Document document = new Document();
 
// 第一个参数是限制条件找到要被更新的bson, 第二个参数是将被改变的值
 collection.deleteMany(document);

SpringDataMongoDB

编写Repository层,进行简单的CRUD

spring-data-mongodb的用法和jpa是一样的,Spring很强大,一统天下

  • 引入启动器
  • 配置文件,和上面的一样
  • 编写DAO层
1
public interface SpitRepository   extends MongoRepository<Spit,String> {}

它内置了简单的增删改查的方法,可以分页,也可以使用Spring对jpql的高级封装比如findByXXX等

MongoTemplate的使用:

更新

如果被更新的文档没有该条记录,就会创建出这个field,然后赋值

1
2
3
4
5
6
7
8
9
10
11
Query query = new Query();
Criteria criteria = new Criteria();
query.addCriteria(Criteria.where("_id").is("5d50cee71e5a931d2cd72e43"));
 
Update update = new Update();
update.set("key1","新的值");
 
// 只会更新满足条件的第一条
入参位置1  query作用: 根据条件找出对应的doc
入参位置2  update作用:  进行更新的操作
mongoTemplate.updateFirst(query,update,"集合");

使用下面的函数也能达到相同更新的效果

1
  mongoTemplate.upsert(query,update,"集合");

批量更新:

批量更新满足条件的doc,只要前面的条件满足, 就会被更新, 不存在的字段会添加上去, 存在的被更新掉

1
2
3
4
5
6
7
8
Query query = new Query();
query.addCriteria(Criteria.where("nickname").is("恩恩"));
 
Update update = new Update();
update.set("share","2");  // 同样, key8不存在,就新添加进去
 
// 只会更新满足条件的第一条
mongoTemplate.updateMulti(query,update,"spit");

仅仅更新满足条件的第一个

1
2
3
4
5
6
7
8
Query query = new Query();
query.addCriteria(Criteria.where("nickname").is("恩恩"));
 
Update update = new Update();
update.set("share","2");  // 同样, key8不存在,就新添加进去
 
// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");

给指定的字段增加指定的数量, 注意点是, 如果尝试对字符串增加指定的数,不报错,但是也不会改变原来的值

1
2
3
4
5
6
7
8
Query query = new Query();
query.addCriteria(Criteria.where("_id").is("1"));
 
Update update = new Update();
update.inc("thumbup",3); // 给指定的字段增加指定的数量
 
// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");

重命名文档的某个key

1
2
3
4
5
6
7
8
9
Query query = new Query();
Criteria criteria = new Criteria();
query.addCriteria(Criteria.where("_id").is("1"));
 
Update update = new Update();
update.rename("_class","新的class"); // 给指定的字段增加指定的数量
 
// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");

移除key

1
update.unset("新的class");

在现有的文档基础上,嵌入文档

1
2
3
4
5
6
7
8
9
10
11
12
13
Query query = new Query();
Criteria criteria = new Criteria();
query.addCriteria(Criteria.where("_id").is("1"));
 
Update update = new Update();
 
Spit spit = new Spit();
spit.set_id("666");
spit.setContent("我的被嵌套进来的文档");
 
update.addToSet("childSpit",spit);
// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");

修改被嵌入的文档

1
2
3
4
5
6
7
8
9
10
11
12
Query query = new Query();
Criteria criteria = new Criteria();
// 通过  添加条件定位出 指定的嵌套文档
query.addCriteria(Criteria.where("_id").is("1").and("childSpit._id").is("666"));
 
Update update = new Update();
 
// todo 通过这样条件  字段名.$.field
update.set("childSpit.$.content","嵌套文档的conntent被改变了");
 
// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");

删除嵌套文档

1
2
3
4
5
6
7
8
9
10
11
Query query = new Query();
Criteria criteria = new Criteria();
// 通过  添加条件定位出 指定的嵌套文档
query.addCriteria(Criteria.where("_id").is("1").and("childSpit._id").is("666"));
 
Update update = new Update();
 
update.unset("childSpit.$");
 
// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");

使用:MongoTemplate 提高效率的场景

在点赞的业务中,给某个帖子点赞的逻辑如下:

  • 前端提交帖子的id, 后端根据id查询一次数据库,把帖子查出来
  • 给帖子的赞数+1
  • 把帖子更新进数据库

前前后后使用了两次查询,下面使用MongoTemplate,可以实现和数据库一次交互,完成更新

1
2
3
4
5
Query query = new Query();
query.addCriteria(Criteria.where("_id").is("前端提交的id"));
Update update = new Update();
update.inc("赞的字段",1); // 自增1
mongoTemplate.updateFirst(query,update,"集合名字");

参考博客:illustriousness
参考博客: tbyang

 

作者:赐我白日梦

出处:https://www.cnblogs.com/ZhuChangwu/p/11339109.html