初入MongoDB

1 MongoDB簡介

1.1 什麼是MongoDB

MongoDB 是一個跨平臺的,面向文檔的數據庫,是當前 NoSQL 數據庫產品中最熱門的一種。它介於關係數據庫和非關係數據庫之間,是非關係數據庫當中功能最豐富,最像關係數據庫的產品。它支持的數據結構非常鬆散,是類似 JSON 的 BSON 格式,因此可以存儲比較複雜的數據類型,MongoDB 的官方網站地址:http://www.mongodb.org/。

1.2 MongoDB特點

對於數據量大、寫入操作頻繁、價值較低這樣的數據,我們更適合使用MongoDB來實現數據的存儲,MongoDB 最大的特點是他支持的查詢語言非常強大,其語法有點類似於面向對象的查詢語言,幾乎可以實現類似關係數據庫單表查詢的絕大部分功能,而且還支持對數據建立索引。它是一個面向集合的,模式自由的文檔型數據庫,具體特點總結如下:
    (1)面向集合存儲,易於存儲對象類型的數據
    (2)模式自由
    (3)支持動態查詢
    (4)支持完全索引,包含內部對象
    (5)支持複製和故障恢復
    (6)使用高效的二進制數據存儲,包括大型對象(如視頻等)
    (7)自動處理碎片,以支持雲計算層次的擴展性
    (8)支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++語言的驅動程序,社區中也提供了對 Erlang 及.NET 等平臺的驅動程序
    (9) 文件存儲格式爲 BSON(一種 JSON 的擴展)


1.3 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)


 

 

1.5 數據類型

  • 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(){/…/}}
     

2 走進MongoDB

2.1 MongoDB安裝與啓動

MongoDB 預編譯二進制包下載地址:https://www.mongodb.com/download-center/community

       安裝過程中,你可以通過點擊 "Custom(自定義)" 按鈕來設置你的安裝目錄。(4.0以下版本安裝方式)

點擊下一步直到安裝完成,爲了方便每次啓動,我將MongoDB安裝後的bin目錄設置到環境變量path中

① 首先打開命令提示符,創建一個用於存放數據的目錄

md  d:\data


       ② 啓動服務(服務器端)
 

mongod ‐‐dbpath=d:\data

啓動日誌

在啓動信息中可以看到,mongoDB的默認端口是27017,如果我們想改變默認的啓動端口,可以通過--port來指定端口。

③ 在命令提示符輸入以下命令即可連接上 MongoDB(客戶端)

mongo

④ 退出mongodb

exit


2.2 Docker 環境下MongoDB安裝
 

在宿主機創建mongo容器  

docker run ‐di ‐‐name=demo_mongo ‐p 27017:27017 mongo

遠程連接

mongo 192.168.92.131

2.3 常用命令

以吐槽表spit爲例演示MongoDB常用命令
 

字段名稱 字段含義 字段類型 備註
_id ID 文本  
content 吐槽內容 文本  
publishtime 發佈日期 日期  
userid 發佈人ID 文本  
nickname 發佈人暱稱 文本  
visits 瀏覽量 整型  
thumbup 點贊數 整型  
share 分享數 整型  
comment 回覆數 整型  
state 是否可見 文本  
parentid 上級ID 文本  
  •  選擇和創建數據庫, 如果不存在就自動創建
use 數據庫名稱
use spitdb
  • 插入集合和文檔,集合如果不存在就自動創建
db.集合名稱.insert(文檔數據)
db.spit.insert({content:"內容1",userid:"7774",nickname:"云云",visits:NumberInt(60)})
  • 查詢集合
db.集合名稱.find()
db.spit.find()

每條文檔會有一個叫_id的字段,這個相當於關係數據庫中表的主鍵,當你在插入文檔記錄時沒有指定該字段,MongoDB會自動創建,其類型是ObjectID類型。如果我們在插入文檔記錄時指定該字段也可以,其類型可以是ObjectID類型,也可以是MongoDB支持的任意類型。如下:

db.spit.insert({_id:"1",content:"內容123",userid:"232232",nickname:"小明"})
  • 條件查詢
db.spit.find(條件)
//查詢userid爲232232的記錄
db.spit.find({userid:"232232"})

如果你只需要返回符合條件的第一條數據,我們可以使用findOne命令來實現
 

db.spit.findOne({nickname:"小明"})

如果你想返回指定條數的記錄,可以在find方法後調用limit來返回結果,例如:

db.spit.find({nickname:"小明"}).limit(3)
  • 修改文檔
db.集合名稱.update(條件,修改後的數據)

執行後,我們會發現,這條文檔除了visits字段其它字段都不見了,爲了解決這個問題,我們需要使用修改器$set來實現,命令如下:
 

db.spit.update({_id:"2"},{$set:{visits:NumberInt(2000)}})
  • 列值增長

如果我們想實現對某列值在原有值的基礎上進行增加或減少,可以使用$inc運算符來實現

db.spit.update({_id:"1"},{$inc:{visits:NumberInt(1)}})

 

  • 刪除文檔
db.集合名稱.remove(條件)
//刪除userid=1211的文檔
db.spit.remove({userid:"1211"})

以下語句可以將數據全部刪除,請慎用 

db.spit.remove({})
  • 統計條數

統計記錄條件使用count()方法。以下語句統計spit集合的記錄數

db.spit.count()//統計全部
db.spit.count({userid:"1013"})//條件統計
  • 模糊查詢

MongoDB的模糊查詢是通過正則表達式的方式實現的。格式爲:

/模糊查詢字符串/

例如,我要查詢吐槽內容包含“內容”的所有文檔,代碼如下:

db.spit.find({content:/內容/})

如果要查詢吐槽暱稱中以“小”開頭的,代碼如下:

db.spit.find({nickname:/^小/})

 

  • 大於 小於 不等於

<, <=, >, >=  這個操作符也是很常用的,格式如下:

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}})

 

  •  包含與不包含

包含使用$in操作符,示例:查詢吐槽集合中userid字段包含1013和1014的文檔

db.spit.find({userid:{$in:["1013","1014"]}})

不包含使用$nin操作符。示例:查詢吐槽集合中userid字段不包含1013和1014的文檔

db.spit.find({userid:{$nin:["1013","1014"]}})
  • 條件連接

我們如果需要查詢同時滿足兩個以上條件,需要使用$and操作符將條件進行關聯。(相當於SQL的and)

格式爲:$and:[ { },{ },{ } ]

示例:查詢吐槽集合中visits大於等於1000 並且小於2000的文檔

db.spit.find({$and:[{visits:{$lt:2000}},{visits:{$gte:1000}}]})

如果兩個以上條件之間是或者的關係,我們使用$or操作符進行關聯,與前面and的使用方式相同

格式爲:$or:[ { },{ },{ } ]

示例:查詢吐槽集合中userid爲1013,或者瀏覽量小於2000的文檔記錄

db.spit.find({$or:[{userid:"1013"},{visits:{$lt:2000}}]})

 

3 Java操作MongoDB

mongodb-driver是mongo官方推出的java連接mongoDB的驅動包,相當於JDBC驅動,我們通過一個入門的案例來了解mongodb-driver的基本使用
      ①  查詢全部記錄

  • 創建工程 mongoDemo, 引入依賴
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>3.8.2</version>
</dependency>
  • 創建測試類
public class MongoDemo {
    public static void main(String[] args) {
        //創建連接
        MongoClient mongoClient = new MongoClient("192.168.92.132");
        //獲取數據庫
        MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
        //獲取集合
        MongoCollection<Document> spits = spitdb.getCollection("spit");
        //獲取文檔數據集合
        FindIterable<Document> documents = spits.find();
        //遍歷集合
        for(Document document:documents){
            //取值時注意值的類型,不然會報類型轉換錯誤
            System.out.println("id:"+document.getObjectId("_id"));
            System.out.println("內容:"+document.getString("content"));
            System.out.println("瀏覽量:"+document.getInteger("visits"));
        }
        //關閉連接
        mongoClient.close();
    }
}

② 條件查詢

BasicDBObject對象:表示一個具體的記錄,BasicDBObject實現了DBObject,是keyvalue的數據結構,用起來和HashMap是基本一致的。

  • 查詢userid爲2732的記錄
public static void main(String[] args) {
    //創建連接
    MongoClient mongoClient = new MongoClient("192.168.92.132");
    //獲取數據庫
    MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
    //獲取集合
    MongoCollection<Document> spits = spitdb.getCollection("spit");
    //構建條件
    BasicDBObject bosn = new BasicDBObject();
    bosn.put("userid","2732");
    //查詢符合條件的文檔記錄
    FindIterable<Document> documents = spits.find(bosn);
    for(Document document:documents){
        //取值時注意值的類型,不然會報類型轉換錯誤
        System.out.println("id:"+document.getObjectId("_id"));
        System.out.println("內容:"+document.getString("content"));
        System.out.println("瀏覽量:"+document.getInteger("visits"));
    }
    //關閉連接
    mongoClient.close();
}
  • 查詢瀏覽量大於1000的記錄
public static void main(String[] args) {
    //創建連接
    MongoClient mongoClient = new MongoClient("192.168.92.132");
    //獲取數據庫
    MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
    //獲取集合
    MongoCollection<Document> spits = spitdb.getCollection("spit");
    //構建條件
    BasicDBObject bosn = new BasicDBObject("visits",new BasicDBObject("$gt",1000));
    //查詢符合條件的文檔記錄
    FindIterable<Document> documents = spits.find(bosn);
    for(Document document:documents){
        //取值時注意值的類型,不然會報類型轉換錯誤
        System.out.println("id:"+document.getObjectId("_id"));
        System.out.println("內容:"+document.getString("content"));
        System.out.println("瀏覽量:"+document.getInteger("visits"));
    }
    //關閉連接
    mongoClient.close();
}

③ 插入記錄

public static void main(String[] args) {
    //創建連接
    MongoClient mongoClient = new MongoClient("192.168.92.132");
    //獲取數據庫
    MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
    //獲取集合
    MongoCollection<Document> spits = spitdb.getCollection("spit");
    //組裝數據
    Map data=new HashMap();
    data.put("content","這是一個內容");
    data.put("userid","3232");
    data.put("nickname","明明");
    data.put("visits",1560);
    Document document = new Document(data);
    spits.insertOne(document);
    //關閉連接
    mongoClient.close();
}

3  SpringDataMongoDB

SpringData家族成員之一,用於操作MongoDb的持久層框架,封裝了底層的mongodbdriver,官網主頁: https://projects.spring.io/spring-data-mongodb

3.1 採用SpringDataMongoDB框架實現吐槽

  ① pom.xml引入依賴  

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter‐data‐mongodb</artifactId>
</dependency>

  ② 創建application.yml

#配置tomcat端口
server.port: 9006
#配置mongodb數據庫連接
spring:
  data:
    mongodb:
      host: 192.168.92.132
      port: 27017
      database: spitdb

  ③ 創建啓動類

@SpringBootApplication
public class SpitApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpitApplication.class);
    }

    @Bean
    public IdWorker idWorker(){
        return new IdWorker(1,1);
    }
}

3.2 基本增刪改查API實現

  ① 創建實體類

public class Spit implements Serializable {
    private String _id;
    private String content;//吐槽內容
    private Date publishtime;//發佈日期
    private String userid;//發佈人ID
    private Integer visits;//瀏覽量
    private Integer thumbup;//點贊數
    private Integer share;//分享數
    private Integer comment;//回覆數
    private String state;//是否可見
    private String parentid;//上級ID

    // getter and setter .....
}

  ② 創建數據訪問接口

/
**
* 吐槽數據訪問層
* @author Administrator
* *
/
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,"刪除成功");
    }
}

3.3 根據上級ID查詢吐槽列表

    ① SpitDao新增方法定義

/**
* 根據上級ID查詢吐槽列表(分頁)
* @param parentid
* @param pageable
* @return
*/
public Page<Spit> findByParentid(String parentid,Pageable pageable)

   ② SpitService新增方法

/**
 * 根據上級ID查詢吐槽數據(分頁)
 * 查詢一個吐槽下面的評論
 */
public PageResult<Spit> findByParentid(String pid,int page,int size){
    Pageable pageable =PageRequest.of(page-1,size);
    Page<Spit> pages = spitDao.findByParentid(pid, pageable);
   return new PageResult<Spit>(pages.getTotalElements(),pages.getContent());

}

 ③ SpitController新增方法

//根據上級ID查詢吐槽數據(分頁)
@GetMapping("/comment/{parentid}/{page}/{size}")
public Result<PageResult> findByParentId(@PathVariable("parentid") String id,@PathVariable int page,@PathVariable int size){
    return new Result<>(StatusCode.SUCCESS,true,"查詢成功",spitService.findByParentid(id,page,size));
}

3.4 吐槽點贊

/**
* 點贊
* @param id
*/
public void updateThumbup(String id){
	Spit spit = spitDao.findById(id).get();
	spit.setThumbup(spit.getThumbup()+1);
	spitDao.save(spit);
}

以上方法雖然實現起來比較簡單,但是執行效率並不高,因爲我只需要將點贊數加1就可以了,沒必要查詢出所有字段修改後再更新所有字段,我們可以使用MongoTemplate類來實現對某列的操作。

 ① 修改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");
}

② SpitController新增方法

/**
* 點贊
* @param id
* @return
*/
@RequestMapping(value="/thumbup/{id}",method=RequestMethod.PUT)
public Result updateThumbup(@PathVariable String id){
    spitService.updateThumbup(id);
    return new Result(true,StatusCode.OK,"點贊成功");
}

3.5 取消點贊

  我們可以通過redis控制用戶點贊或者取消點贊

  ① pom.xml中加入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter‐data‐redis</artifactId>
</dependency>

  ② 修改application.yml

redis:
  host: 192.168.184.135

  ③  修改SpitService 點贊方法

//吐槽點贊
public String  updateThumbup(String id) {
    String message="";
    Query query = new Query();
    //添加條件
    query.addCriteria(Criteria.where("_id").is(id));
    Update update=new Update();
    String userid="7777";//模擬數據,實際應該取當前登陸的用戶
    if("1".equals(redisTemplate.opsForValue().get("thumbup_"+userid+"_"+id))){
        //點贊取消
        update.inc("thumbup",-1);
        redisTemplate.opsForValue().set("thumbup_"+userid+"_"+id,"-1");
        message="點贊已取消";

    }else {
        //點贊
        update.inc("thumbup",1);
        redisTemplate.opsForValue().set("thumbup_"+userid+"_"+id,"1");
        message="點贊成功";
    }

    mongoTemplate.updateFirst(query,update,"spit" );
    return  message;
}

3.6 發佈吐槽

修改SpitService的add方法

/**
* 發佈吐槽(或吐槽評論)
* @param spit
*/
public void add(Spit spit){
    spit.set_id( idWorker.nextId()+"" );
    spit.setPublishtime(new Date());//發佈日期
    spit.setVisits(0);//瀏覽量
    spit.setShare(0);//分享數
    spit.setThumbup(0);//點贊數
    spit.setComment(0);//回覆數
    spit.setState("1");//狀態
    if(spit.getParentid()!=null && !"".equals(spit.getParentid())){
    //如果存在上級ID,則是評論
    Query query=new Query();
    query.addCriteria(Criteria.where("_id").is(spit.getParentid()));
        Update update=new Update();
        update.inc("comment",1);
        mongoTemplate.updateFirst(query,update,"spit");
    } 
    spitDao.save(spit);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章