上手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
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,"集合名字");