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 , 前面有个下划线
使用命令行进行数据的读写
点击文档官网 https://docs.mongodb.com/
开启本地的 mongo 服务器 (用来保存数据) 打开之后, 就别关了
默认使用的端口是 27017
mongod -dbpath = 指定文件夹当作数据存储路径 --port 端口号
连接数据库(客户端, 进行 CRUD)
- // 在本机直接用下面的命令, 模拟连接本机的 MongoDB
- mongo
- // 连接远程的 mongo
- mongo 192.168.88.88
退出
exit
选择数据库
默认选中的数据库是 test 库
// 如果数据名不存在, 默认直接把它创建出来, 并且直接选中它使用
user 数据库名
查看当前数据库
db
保存一条数据
入参位置的 bson, 注意 key 的部分是没有双引号的
db. 集合名. insert({content:"哈哈, 保存了数据"})
查询全部数据, 默认按照_id 从小到大排序
db. 集合名. find()
返回满足条件的全部数据
入参部分的查询条件 bson 同样是 JSON 格式
db. 集合名. find({id:"123"})
返回满足条件的第一条数据
入参部分的查询条件 bson 同样是 JSON 格式
db. 集合名. findOne({id:"123"})
根据限制的数量, 返回满足条件的数据
入参部分的查询条件 bson 同样是 JSON 格式
db. 集合名. find({id:"123"}).limit(1)
更新文档
db. 集合名. update(条件, 修改后的数据)
// 下面的这条修改语句存在问题, 只会保留 name 字段
db. 集合. update({_id:"1"},{name:"新名字"})
// 保留其他字段的写法 db. 集合名. update({条件},{$set:{新的值} })
db. 集合. update({_id:"1"},{$set:{name:"新名字"}})
删除文档
db. 集合. remove(条件)
// 删除全部
db. 集合. remove({})
统计数量
- db.spit.count()
- db.spit.count({
- 条件
- })
使用正则实现. 模糊查询
/ 将被模糊查询的字符串 /
db. 集合名. find({content:/ 将被模糊查询的字符串 /})
// 匹配以 XXX 开头的文档
db. 集合名. find({content:/^XXX/})
大于, 小于, 不等于
db. 集合名. find({"参考的字段":{$gt:value}}) // 查找参考的字段大于 value 值的文档
$lt 小于
$lte 小于等于
$gte 大于等于
$ne 不等于
包含和不包含
db. 集合名. find({_id:{$in:["1","2","3"]}})
db. 集合名. find({_id:{$nin:["1","2","3"]}})
条件连接
- $and:[{
- },{
- },{
- }]
- // 查询 id 大于 1000, 小于 2000 的文档
db. 集合名. find({$and:[{_id:{$gte:1000}},{_id:{$lte:2000}}]})
$or:[{},{},{}]
实现列值的增长
db. 集合名. update({_id:"12"},{$inc:{number:NumberInt(1)}})
按照某个字段排序
db. 集合名. find({}).sort(字段: 1) // 1 是升序
db. 集合名. find({}).sort(字段:-1) // -1 是降序
// 先按照字段 1 升序, 如果字段 1 的值相同, 按照字段 2 降序排序
db. 集合名. find({}).sort(字段: 1| 字段 2:-1)
Java 操作 MongoDB
添加坐标
- <dependency>
- <groupId>org.MongoDB</groupId>
- <artifactId>MongoDB-driver</artifactId>
- <version>3.6.3</version>
- </dependency>
配置文件
- server:
- port: 9002
- data:
- MongoDB:
- host: 192.168.88.130
database: 数据库名字
使用 mongo 官方提供的 java 的驱动包, 相当于 jdbc
- // 连接 mongo 服务器
- MongoClient client = new MongoClient("192.168.88.130");
- // 得到想操作的数据库
- MongoDatabase db = client.getDatabase("数据库名");
- // 得到想要操作的集合
- MongoCollection<Document> collection = db.getCollection("集合名");
- // todo CRUD with collection
- // 释放资源
- client.close();
查询全部
- FindIterable<Document> documents = collection.find();
- for (Document document : documents) {
- System.out.println(document);
- }
根据限制条件查询
查询出 content 字段为 "我很开心" 全部文档
- // 条件查询限制为 userid==123
- BasicDBObject bson = new BasicDBObject("content", "我很开心");
- FindIterable<Document> documents = collection.find(bson);
封装条件, 查询访问量大于 1000 的, 可以还可以更换成 gt gte 等等
- // find(visit:{
- $gt:1000
- })
- BasicDBObject bson = new BasicDBObject("visits", new BasicDBObject("$gt",1000));
- FindIterable<Document> documents = collection.find(bson);
我发现如果是使用_id 当作限制条件, 此时如果_id 的类型是原生的 objectid 类型的话, 是查询不处任何结果的, 但是如果换成我们自定义的_id, 就可以当成限制条件匹配出相应的文档
添加一个文档
_id 不能重复
- HashMap<String, Object> map = new HashMap<>();
- map.put("_id","10");
- map.put("content","啊");
- map.put("article","嘻嘻嘻嘻嘻");
- Document document = new Document(map);
- collection.insertOne(document);
添加嵌套文档
- 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)
更新
更新数值类型的数, 在原来的基础上增加
- // 使用 $inc , 在将入参 2 位置对应的文档的指定字段增加指定的数
- BasicDBObject newDoc = new BasicDBObject().append("$inc",new BasicDBObject().append("thumbup",1));
- // 第一个参数是限制条件找到要被更新的 bson, 第二个参数是将被改变的值
- collection.updateOne(new BasicDBObject().append("content","我是新的 content"),newDoc);
更新普通字符串类型
- // 使用 $set , 在将入参 2 位置对应的文档的指定字段更新为指定的值
- BasicDBObject newDoc = new BasicDBObject().append("$set",new BasicDBObject().append("nickname","朱昌武"));
- // 第一个参数是限制条件找到要被更新的 bson, 第二个参数是将被改变的值
- collection.updateOne(new BasicDBObject().append("_id","1"),newDoc);
替换文档
同样, 如果入参 1 位置的过滤条件, 使用的_id 是原生 ObjectId 类型的话, 匹配不出任何结果
- 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);
删除文档
- // 删除一个
- 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 层
public interface SpitRepository extends MongoRepository<Spit,String> {}
它内置了简单的增删改查的方法, 可以分页, 也可以使用 Spring 对 jpql 的高级封装比如 findByXXX 等
MongoTemplate 的使用:
更新
如果被更新的文档没有该条记录, 就会创建出这个 field, 然后赋值
- 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,"集合");
使用下面的函数也能达到相同更新的效果
mongoTemplate.upsert(query,update,"集合");
批量更新:
批量更新满足条件的 doc, 只要前面的条件满足, 就会被更新, 不存在的字段会添加上去, 存在的被更新掉
- Query query = new Query();
- query.addCriteria(Criteria.where("nickname").is("恩恩"));
- Update update = new Update();
- update.set("share","2"); // 同样, key8 不存在, 就新添加进去
- // 只会更新满足条件的第一条
- mongoTemplate.updateMulti(query,update,"spit");
仅仅更新满足条件的第一个
- Query query = new Query();
- query.addCriteria(Criteria.where("nickname").is("恩恩"));
- Update update = new Update();
- update.set("share","2"); // 同样, key8 不存在, 就新添加进去
- // 只会更新满足条件的第一条
- mongoTemplate.updateFirst(query,update,"spit");
给指定的字段增加指定的数量, 注意点是, 如果尝试对字符串增加指定的数, 不报错, 但是也不会改变原来的值
- 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
- 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
update.unset("新的 class");
在现有的文档基础上, 嵌入文档
- 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");
修改被嵌入的文档
- 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");
删除嵌套文档
- 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, 可以实现和数据库一次交互, 完成更新
- 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