一、MongoDB簡介
MongoDB 是一個跨平臺的,面向文檔的數據庫,是當前 NoSQL 數據庫產品中最熱門的一種。它介於關係數據庫和非關係數據庫之間,是非關係數據庫當中功能最豐富,最像關係數據庫的產品。它支持的數據結構非常鬆散,是類似 JSON 的 BSON 格式,因此可以存儲比較複雜的數據類型。 MongoDB 的官方網站地址是:http://www.mongodb.org/
MongoDB適合存儲哪些數據
什麼時候適合使用MongoDB?
- 數據量大
- 寫入操作頻繁
- 數據的價值較低
針對具有上邊特點的數據,我們更適合使用MongoDB來實現數據的存儲;
MongoDB特點
MongoDB 最大的特點是他支持的查詢語言非常強大,其語法有點類似於面向對象的查詢語言,幾乎可以實現類似關係數據庫單表查詢的絕大部分功能,而且還支持對數據建立索引。它是一個面向集合的、模式自由的文檔型數據庫。
具體特點總結如下:
- 面向集合存儲,易於存儲對象類型的數據
- 模式自由
- 支持動態查詢
- 支持完全索引,包含內部對象
- 支持複製和故障恢復
- 使用高效的二進制數據存儲,包括大型對象(如視頻等)
- 自動處理碎片,以支持雲計算層次的擴展性
- 支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++語言的驅動程
序,社區中也提供了對 Erlang 及.NET 等平臺的驅動程序 - 文件存儲格式爲 BSON(一種 JSON 的擴展)
MongoDB體系結構
MongoDB 的邏輯結構是一種層次結構。主要由:
文檔(document)、集合(collection)、數據庫(database)這三部分組成的。邏輯結構是面
向用戶的,用戶使用 MongoDB 開發應用程序使用的就是邏輯結構。
(1)MongoDB 的文檔(document),相當於關係數據庫中的一行記錄。
(2)多個文檔組成一個集合(collection),相當於關係數據庫的表。
(3)多個集合(collection),邏輯上組織在一起,就是數據庫(database)。
(4)一個 MongoDB 實例支持多個數據庫(database)。
文檔(document)、集合(collection)、數據庫(database)的層次結構如下圖:
下表是MongoDB與MySQL數據庫邏輯結構概念的對比
MongoDb | 關係型數據庫Mysql |
---|---|
數據庫(databases) | 數據庫(databases) |
集合(collections) | 表(table) |
文檔(document) | 行(row) |
數據類型
- null:用於表示空值或者不存在的字段,{“x”:null}
- 布爾型:布爾類型有兩個值true和false,{“x”:true}
- 數值:shell默認使用64位浮點型數值。{“x”:3.14}或{“x”:3}。對於整型值,可以使用
NumberInt(4字節符號整數)或NumberLong(8字節符號整數),{“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)} - 字符串:UTF-8字符串都可以表示爲字符串類型的數據,{“x”:“呵呵”}
- 日期:日期被存儲爲自新紀元依賴經過的毫秒數,不存儲時區,{“x”:new Date()}
- 正則表達式:查詢時,使用正則表達式作爲限定條件,語法與JavaScript的正則表達式相
同,{“x”:/[abc]/} - 數組:數據列表或數據集可以表示爲數組,{“x”: [“a“,“b”,”c”]}
- 內嵌文檔:文檔可以嵌套其他文檔,被嵌套的文檔作爲值來處理,{“x”:{“y”:3 }}
- 對象Id:對象id是一個12字節的字符串,是文檔的唯一標識,{“x”: objectId() }
- 二進制數據:二進制數據是一個任意字節的字符串。它不能直接在shell中使用。如果要
將非utf-字符保存到數據庫中,二進制數據是唯一的方式。 - 代碼:查詢和文檔中可以包括任何JavaScript代碼,{“x”:function(){/…/}}
二、走進MongoDB
2.1 MongoDB安裝與啓動
2.1.1 window系統MongoDB安裝
官網下載MongoDB
打開官網:http://www.mongodb.org/
點擊下載按鈕
安裝
雙擊下載下來的“mongodb-win32-x86_64-2008plusssl-3.2.10-signed.msi” ,按照提示步驟安裝即可。安裝完成後,軟件會安裝在C:\ProgramFiles\MongoDB 目錄中。
我們要啓動的服務程序就是C:\Program Files\MongoDB\Server\3.2\bin目錄下的mongod.exe,爲了方便我們每次啓動,我將C:\ProgramFiles\MongoDB\Server\3.2\bin 設置到環境變量path中。
啓動
(1)首先打開命令提示符,創建一個用於存放數據的目錄
md d:\data
(2)啓動服務
mongod ‐‐dbpath=d:\data
我們在啓動信息中可以看到,mongoDB的默認端口是27017,如果我們想改變默認的啓動端口,可以通過–port來指定端口;
在命令提示符輸入以下命令即可完成登陸
mongo
退出mongodb
exit
2.1.2 Docker 環境下MongoDB安裝
我們可以用 docker search mongo 命令來查看可用版本
docker search mongo
這裏我們拉取官方的最新版本的鏡像:
$ docker pull mongo:latest
在宿主機創建mongo容器
docker run ‐di ‐‐name=tensquare_mongo ‐p 27017:27017 mongo
遠程登陸
mongo 192.168.184.134
2.2 常用命令
2.2.1 選擇和創建數據庫
選擇和創建數據庫的語法格式:
use 數據庫名稱
如果數據庫不存在則自動創建
以下語句創建spit數據庫
use spitdb
2.2.2 插入與查詢文檔
插入文檔的語法格式:
db.集合名稱.insert(數據);
我們這裏可以插入以下測試數據:
db.spit.insert({content:"你好,這裏是主要信息",userid:"1011",nickname:"小明",visits:NumberInt(902)})
查詢集合的語法格式:
db.集合名稱.find()
如果我們要查詢spit集合的所有文檔,我們輸入以下命令
db.spit.find()
這裏你會發現每條文檔會有一個叫_id的字段,這個相當於我們原來關係數據庫中表的主
鍵,當你在插入文檔記錄時沒有指定該字段,MongoDB會自動創建,其類型是ObjectID
類型。如果我們在插入文檔記錄時指定該字段也可以,其類型可以是ObjectID類型,也
可以是MongoDB支持的任意類型。
輸入以下測試語句:
db.spit.insert({_id:"1",content:"我還是沒有想明白到底爲啥出錯",userid:"1012",nickname:"小明",visits:NumberInt(2020)});
db.spit.insert({_id:"2",content:"加班到半夜",userid:"1013",nickname:"凱撒",visits:NumberInt(1023)});
db.spit.insert({_id:"3",content:"手機流量超了咋辦?",userid:"1013",nickname:"凱撒",visits:NumberInt(111)});
db.spit.insert({_id:"4",content:"堅持就是勝利",userid:"1014",nickname:"諾諾",visits:NumberInt(1223)});
如果我想按一定條件來查詢,比如我想查詢userid爲1013的記錄,怎麼辦?很簡單!只 要在find()中添加參數即可,參數也是json格式,如下:
db.spit.find({userid:'1013'})
如果你只需要返回符合條件的第一條數據,我們可以使用findOne命令來實現
db.spit.findOne({userid:'1013'})
如果你想返回指定條數的記錄,可以在find方法後調用limit來返回結果,例如:
db.spit.find().limit(3)
2.2.3 修改與刪除文檔
修改文檔的語法結構:
db.集合名稱.update(條件,修改後的數據)
如果我們想修改_id爲1的記錄,visits字段值改爲1000,輸入以下語句:
db.spit.update({_id:"1"},{visits:NumberInt(1000)})
執行後,我們會發現,這條文檔除了visits字段其它字段都不見了,爲了解決這個問題,我們需要使用修改器$set來實現,命令如下:
db.spit.update({_id:"2"},{$set:{visits:NumberInt(2000)}})
這樣就OK啦。
刪除文檔的語法結構:
db.集合名稱.remove(條件)
以下語句可以將數據全部刪除,請慎用
db.spit.remove({})
如果刪除visits字段值爲1000的記錄,輸入以下語句
db.spit.remove({visits:1000})
2.2.4 統計條數
統計記錄條件使用count()方法。以下語句統計spit集合的記錄數
db.spit.count()
如果按條件統計 ,例如:統計userid爲1013的記錄條數
db.spit.count({userid:"1013"})
2.2.5 模糊查詢
MongoDB的模糊查詢是通過正則表達式的方式實現的。格式爲:
/模糊查詢字符串/
例如,我要查詢content字段的內容包含“流量”的所有文檔,代碼如下:
db.spit.find({content:/流量/})
如果要查詢content字段的內容中以“加班”開頭的,代碼如下:
db.spit.find({content:/^加班/})
2.2.6 大於 小於 不等於
<, <=, >, >= 這個操作符也是很常用的,格式如下:
db.集合名稱.find({ "field" : { $gt: value }}) // 大於: field > value
db.集合名稱.find({ "field" : { $lt: value }}) // 小於: field < value
db.集合名稱.find({ "field" : { $gte: value }}) // 大於等於: field >= value
db.集合名稱.find({ "field" : { $lte: value }}) // 小於等於: field <= value
db.集合名稱.find({ "field" : { $ne: value }}) // 不等於: field != value
示例:查詢字段的值大於1000的記錄
db.spit.find({visits:{$gt:1000}})
2.2.7 包含與不包含
包含使用$in操作符。
示例:查詢集合中userid字段包含1013和1014的文檔
db.spit.find({userid:{$in:["1013","1014"]}})
不包含使用$nin操作符。
示例:查詢集合中userid字段不包含1013和1014的文檔
db.spit.find({userid:{$nin:["1013","1014"]}})
2.2.8 條件連接
我們如果需要查詢同時滿足兩個以上條件,需要使用$and操作符將條件進行關聯。(相
當於SQL的and)
格式爲:
$and:[ { },{ },{ } ]
示例:查詢集合中visits大於等於1000 並且小於2000的文檔
db.spit.find({$and:[ {visits:{$gte:1000}} ,{visits:{$lt:2000} }]})
如果兩個以上條件之間是或者的關係,我們使用 操作符進行關聯,與前面and的使用
方式相同
格式爲:
$or:[ { },{ },{ } ]
示例:查詢吐槽集合中userid爲1013,或者瀏覽量小於2000的文檔記錄
db.spit.find({$or:[ {userid:"1013"} ,{visits:{$lt:2000} }]})
2.2.9 列值增長
如果我們想實現對某列值在原有值的基礎上進行增加或減少,可以使用$inc運算符來實現
db.spit.update({_id:"2"},{$inc:{visits:NumberInt(1)}} )
三、Java操作MongoDB
3.1 mongodb-driver
mongodb-driver是mongo官方推出的java連接mongoDB的驅動包,相當於JDBC驅動。我們通過一個入門的案例來了解mongodb-driver的基本使用
3.1.1 查詢全部記錄
(1)創建工程 mongoDemo, 引入依賴
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb‐driver</artifactId>
<version>3.6.3</version>
</dependency>
</dependencies>
(2)創建測試類
/**
* MongoDb入門小demo
*/
public class MongoDemo {
public static void main(String[] args) {
//創建連接
MongoClient client=new MongoClient("192.168.184.134");
//打開數據庫
MongoDatabase spitdb = client.getDatabase("spitdb");
//獲取集合
MongoCollection<Document> spit = spitdb.getCollection("spit");
//查詢記錄獲取文檔集合
FindIterable<Document> documents = spit.find();
for(Document document:documents){
System.out.println("內容:"+ document.getString("content"));
System.out.println("用戶ID:"+document.getString("userid"));
System.out.println("瀏覽量:"+document.getInteger("visits"));
}
client.close();//關閉連接
}
}
3.1.2 條件查詢
BasicDBObject對象:表示一個具體的記錄,BasicDBObject實現了DBObject,是key-value的數據結構,用起來和HashMap是基本一致的。
(1)查詢userid爲1013的記錄
public class MongoDemo1 {
public static void main(String[] args) {
//創建連接
MongoClient client=new MongoClient("192.168.184.134");
//打開數據庫
MongoDatabase spitdb = client.getDatabase("spitdb");
//獲取集合
MongoCollection<Document> spit = spitdb.getCollection("spit");
// 構建查詢條件
BasicDBObject bson=new BasicDBObject("userid","1013");
//查詢記錄獲取結果集合
FindIterable<Document> documents = spit.find(bson);
for(Document document:documents){ //
System.out.println("內容:"+ document.getString("content"));
System.out.println("用戶ID:"+document.getString("userid"));
System.out.println("瀏覽量:"+document.getInteger("visits"));
}
client.close();//關閉連接
}
}
(2)查詢visits字段大於1000的記錄
public class MongoDemo2 {
public static void main(String[] args) {
//創建連接
MongoClient client=new MongoClient("192.168.184.134");
//打開數據庫
MongoDatabase spitdb = client.getDatabase("spitdb");
//獲取集合
MongoCollection<Document> spit = spitdb.getCollection("spit");
// 構建查詢條件
BasicDBObject bson=new BasicDBObject("visits",new BasicDBObject("$gt",1000) );
//查詢記錄獲取結果集合
FindIterable<Document> documents = spit.find(bson);
for(Document document:documents){ //
System.out.println("內容:"+ document.getString("content"));
System.out.println("用戶ID:"+document.getString("userid"));
System.out.println("瀏覽量:"+document.getInteger("visits"));
}
//關閉連接
client.close();
}
}
3.1.3 插入數據
public class MongoDemo3 {
public static void main(String[] args) {
//創建連接
MongoClient client=new MongoClient("192.168.184.134");
//打開數據庫
MongoDatabase spitdb = client.getDatabase("spitdb");
//獲取集合
MongoCollection<Document> spit = spitdb.getCollection("spit");
Map<String,Object> map=new HashMap();
map.put("content","我要吐槽");
map.put("userid","9999");
map.put("visits",123);
map.put("publishtime",new Date());
Document document=new Document(map);
//插入數據
spit.insertOne(document);
client.close();
}
}
四、SpringDataMongoDB框架
4.1、介紹
SpringDataMongoDB是SpringData家族成員之一,用於操作MongoDb的持久層框架,封裝了底層的mongodbDriver。
官方主頁:
https://spring.io/projects/spring-data-mongodb
接下來我們搭建一個小項目,使用SpringDataMongoDB框架來操作MongoDB
4.2、搭建項目
pom.xml引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐data‐mongodb</artifactId>
</dependency>
application.yml
server:
port: 9006
spring:
application:
name: tensquare‐spi
data:
mongodb:
host: 192.168.184
database: spitdb
創建啓動類
@SpringBootApplication
public class SpitApplication {
public static void main(String[] args) {
SpringApplication.run(SpitApplication.class, args);
}
}
4.2.2基本增刪改查API實現
創建實體類
public class Spit implements Serializable{
@Id
private String _id;
private String content;
private Date publishtime;
private String userid;
private String nickname;
private Integer visits;
private Integer thumbup;
private Integer share;
private Integer comment;
private String state;
private String parentid;
// getter and setter .....
}
創建數據訪問接口
注意:這個接口繼承了MongoRepository
public interface SpitDao extends MongoRepository<Spit, String>{
}
創建業務邏輯類
@Service
public class SpitService {
@Autowired
private SpitDao spitDao;
@Autowired
private IdWorker idWorker;
/**
* 查詢全部記錄
* @return
*/
public List<Spit> findAll(){
return spitDao.findAll();
}
/**
* 根據主鍵查詢實體
* @param id
* @return
*/
public Spit findById(String id){
Spit spit = spitDao.findById(id).get();
return spit;
}
/**
* 增加
* @param spit
*/
public void add(Spit spit) {
spit.set_id(idWorker.nextId()+""); //主鍵值
spitDao.save(spit);
}
/**
* 修改
* @param spit
*/
public void update(Spit spit) {
spitDao.save(spit);
}
/**
* 刪除
* @param id
*/
public void deleteById(String id) {
spitDao.deleteById(id);
}
}
創建controller
@RestController
@CrossOrigin
@RequestMapping("/spit")
public class SpitController {
@Autowired
private SpitService spitService;
/**
* 查詢全部數據
* @return
*/
@RequestMapping(method= RequestMethod.GET)
public Result findAll(){
return new Result(true, StatusCode.OK,"查詢成功",spitService.findAll());
}
/**
* 根據ID查詢
* @param id ID
* @return
*/
@RequestMapping(value="/{id}",method=RequestMethod.GET)
public Result findOne(@PathVariable String id){
return new Result(true,StatusCode.OK,"查詢成功",spitService.findById(id));
}
/**
* 增加
* @param spit
*/
@RequestMapping(method=RequestMethod.POST)
public Result add(@RequestBody Spit spit ){
spitService.add(spit);
return new Result(true,StatusCode.OK,"增加成功");
}
/**
* 修改
* @param spit
*/
@RequestMapping(value="/{id}",method=RequestMethod.PUT)
public Result update(@RequestBody Spit spit,@PathVariable String id )
{
spit.set_id(id);
spitService.update(spit);
return new Result(true,StatusCode.OK,"修改成功");
}
/**
* 刪除
* @param id
*/
@RequestMapping(value="/{id}",method=RequestMethod.DELETE)
public Result deleteById(@PathVariable String id ){
spitService.deleteById(id);
return new Result(true,StatusCode.OK,"刪除成功");
}
}
4.2.3 根據上級ID查詢吐槽列表
SpitDao新增方法定義
/**
* 根據上級ID查詢吐槽列表(分頁)
* @param parentid
* @param pageable
* @return
*/
public Page<Spit> findByParentid(String parentid,Pageable pageable);
service與Controller省略…
4.2.4、更新MongoDB
SpitService 新增updateThumbup方法(將Thumbup字段加1後更新保存到數據庫):
public void updateThumbup(String id){
Spit spit = spitDao.findById(id).get();
spit.setThumbup(spit.getThumbup()+1);
spitDao.save(spit);
}
以上方法雖然實現起來比較簡單,但是執行效率並不高,因爲我只需要將Thumbup字段加1就可
以了,沒必要查詢出所有字段修改後再更新所有字段。
我們可以使用MongoTemplate類來實現對某列的操作。
(1)修改SpitService
@Autowired
private MongoTemplate mongoTemplate;
/**
* 點贊
* @param id
*/
public void updateThumbup(String id){
Query query=new Query();
query.addCriteria(Criteria.where("_id").is(id));
Update update=new Update();
update.inc("thumbup",1);
mongoTemplate.updateFirst(query,update,"spit");
}
Controller省略…