MongoDB-基礎使用(二)

前置文章:
MongoDB-基礎使用(一),該文主要介紹MongoDB概念、安裝、庫/集合(表)/文檔(行)操作。

零、本文綱要

一、索引

  1. 索引類型
  2. 索引管理操作
  3. 索引使用

二、API操作-快速入門

  1. pom.xml
  2. application.yml
  3. 啟動類
  4. 測試
  5. 創建測試集合實體類
  6. 持久層接口&業務層編寫
  7. 測試
  8. 根據上級ID查詢分頁列表
  9. MongoTemplate

一、索引

MongoDB索引使用B樹數據結構(確切的說是B-Tree,MySQL是B+Tree)

注意:該部分的內容與Redis、JS類似,整體特性又與MySQL相近。可以簡單了解以下。

1. 索引類型

a、單字段索引
b、復合索引
c、地理空間索引
d、文本索引
e、哈希索引

2. 索引管理操作

① 索引查看

# 索引查看
db.collection.getIndexes()
#如:
db.collection_test.getIndexes()
> db.collection_test.getIndexes()
[
    {
        "v" : 2,                            #索引的版本號
        "key" : {                           #索引的字段
            "_id" : 1                       #1代表升序
        },
        "name" : "_id_",                    #索引的名稱,默認為 "字段名_" + "升降序規則數字",如:"userid_1_nickname_-1"
        "ns" : "db_test.collection_test"    #命名空間,數據庫.集合
    }
]

② 索引創建

# 創建單字段索引
db.collection.createIndex(keys, options)
#如:
db.collection_test.createIndex({userid: 1}) #此處1代表升序,-1則為降序,單字段索引升降序不影響整體查詢效率

# 創建復合索引
#如:
db.collection_test.createIndex({userid: 1, nickname: -1})

以下了解即可:

參數 類型 描述
background Boolean 建索引過程會阻塞其它數據庫操作,background可指定以后臺方式創建索引,即增加 "background" 可選參數。 "background" 默認值為false。
unique Boolean 建立的索引是否唯一。指定為true創建唯一索引。默認值為false.
name string 索引的名稱。如果未指定,MongoDB的通過連接索引的字段名和排序順序生成一個索引名稱。
dropDups Boolean 3.0+版本已廢棄。在建立唯一索引時是否刪除重復記錄,指定 true 創建唯一索引。默認值為 false.
sparse Boolean 對文檔中不存在的字段數據不啟用索引;這個參數需要特別注意,如果設置為true的話,在索引字段中不會查詢出不包含對應字段的文檔.。默認值為 false.
expireAfterSeconds integer 指定一個以秒為單位的數值,完成 TTL設定,設定集合的生存時間。
v index version 索引的版本號。默認的索引版本取決于mongod創建索引時運行的版本。
weights document 索引權重值,數值在 1 到 99,999 之間,表示該索引相對于其他索引字段的得分權重。
default_language string 對于文本索引,該參數決定了停用詞及詞干和詞器的規則的列表。 默認為英語
language_override string 對于文本索引,該參數指定了包含在文檔中的字段名,語言覆蓋默認的language,默認值為 language.

③ 索引移除

# 索引移除
db.collection.dropIndex(index)
#如:
db.collection_test.dropIndex("userid_1")
db.collection_test.dropIndex({userid: 1})

# 移除所有索引(默認"_id_"索引不會被移除)
db.collection_test.dropIndexes()

3. 索引使用

① 執行計劃

# 執行計劃
db.collection.find(query,options).explain(options)
#如:
db.collection_test.find({userid: "1003"}).explain()

"stage" : "COLLSCAN", 表示全集合掃描
"stage" : "IXSCAN" ,基于索引的掃描

② 涵蓋查詢(索引覆蓋)

db.collection_test.find(
    {userid: "1003"},
    {userid:1,_id:-1}
).explain()

二、API操作-快速入門

注意:該部分內容與JPA、MyBatis類似,簡單了解即可。使用的時候能查詢資料,能看懂就可以。

1. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.stone</groupId>
    <artifactId>mongodb-springboot</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <lombok.version>1.18.22</lombok.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
    </dependencies>

</project>

2. application.yml

spring:
#數據源配置
  data:
    mongodb:
      # 主機地址
      host: 192.168.253.128
      # 數據庫
      database: test_db
      # 默認端口是27017
      port: 27017
      #也可以使用uri連接
      #uri: mongodb://192.168.253.128:27017/test_db

3. 啟動類

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

4. 測試

此時,只要能正常連接,不報錯即可。

5. 創建測試集合實體類

① @Document注解

類名小寫與集合名一致則可以省略屬性配置,如果省略,則默認使用類名小寫映射集合;
假設集合名為"comment",則直接使用@Document即可。

② @CompoundIndex注解

@CompoundIndex(def = "{'userid': 1, 'nickname': -1}"),用于指定復合索引;
還是推薦在client通過指令提前生成索引,不推薦使用注解生成索引。

③ @Id注解

主鍵標識,該屬性的值會自動對應mongodb的主鍵字段"_id";
如果該屬性名就叫“id”,則該注解可以省略,否則必須寫。

④ @Field注解

該屬性對應mongodb的字段的名字,如果一致,則無需該注解。

⑤ @Indexed注解

添加了一個單字段的索引;
還是推薦在client通過指令提前生成索引,不推薦使用注解生成索引。

/**
 * 文章評論實體類
 */
//把一個java類聲明為mongodb的文檔,可以通過collection參數指定這個類對應的文檔。
//@Document(collection="mongodb 對應 collection 名")
// 若未加 @Document ,該 bean save 到 mongo 的 comment collection
// 若添加 @Document ,則 save 到 comment collection
@Document(collection="collection_test")//類名小寫與集合名一致則可以省略,如果省略,則默認使用類名小寫映射集合
//復合索引
// @CompoundIndex( def = "{'userid': 1, 'nickname': -1}")
@Data //lombok的注解
public class Comment implements Serializable {
    //主鍵標識,該屬性的值會自動對應mongodb的主鍵字段"_id",如果該屬性名就叫“id”,則該注解可以省略,否則必須寫
    @Id //此處其實可以省略
    private String id;//主鍵
    //該屬性對應mongodb的字段的名字,如果一致,則無需該注解
    @Field("content") //此處該注解其實可以省略
    private String content;//吐槽內容
    private Date publishtime;//發布日期
    //添加了一個單字段的索引
    @Indexed
    private String userid;//發布人ID
    private String nickname;//昵稱
    private LocalDateTime createdatetime;//評論的日期時間
    private Integer likenum;//點贊數
    private Integer replynum;//回復數
    private String state;//狀態
    private String parentid;//上級ID
    private String articleid;
}

6. 持久層接口&業務層編寫

① 持久層接口

該接口需要繼承MongoRepository接口,并指定實體類、主鍵類型,如下:

public interface CommentRepository extends MongoRepository<Comment, String> {
}

MongoRepository接口,如下:

@NoRepositoryBean
public interface MongoRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

    @Override
    <S extends T> List<S> saveAll(Iterable<S> entities);

    @Override
    List<T> findAll();

    @Override
    List<T> findAll(Sort sort);

    <S extends T> S insert(S entity);

    <S extends T> List<S> insert(Iterable<S> entities);

    @Override
    <S extends T> List<S> findAll(Example<S> example);

    @Override
    <S extends T> List<S> findAll(Example<S> example, Sort sort);

可以看到基礎的插入、查詢操作都有,運行時則會生成其代理對象執行具體方法。

② 業務層

public interface CommentService {
    public void saveComment(Comment comment);
    public void updateComment(Comment comment);
    public void deleteCommentById(String id);
    public List<Comment> findCommentList();
    public Comment findCommentById(String id);
}

/**
 * 評論的業務層
 */
@Service
public class CommentServiceImpl implements CommentService {

    //注入dao
    @Autowired
    private CommentRepository commentRepository;

    /**
     * 保存一個評論
     * @param comment
     */
    public void saveComment(Comment comment){
        //如果需要自定義主鍵,可以在這里指定主鍵;如果不指定主鍵,MongoDB會自動生成主鍵
        //設置一些默認初始值。。。
        //調用dao
        commentRepository.save(comment);
    }

    /**
     * 更新評論
     * @param comment
     */
    public void updateComment(Comment comment){
        //調用dao
        commentRepository.save(comment);
    }

    /**
     * 根據id刪除評論
     * @param id
     */
    public void deleteCommentById(String id){
        //調用dao
        commentRepository.deleteById(id);
    }

    /**
     * 查詢所有評論
     * @return
     */
    public List<Comment> findCommentList(){
        //調用dao
        return commentRepository.findAll();
    }

    /**
     * 根據id查詢評論
     * @param id
     * @return
     */
    public Comment findCommentById(String id){
        //調用dao
        return commentRepository.findById(id).get();
    }
}

7. 測試

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MongodbApplication.class)
public class CommentServiceImplTest {
    //注入Service
    @Autowired
    private CommentService commentService;
    /**
     * 保存一個評論
     */
    @Test
    public void testSaveComment(){
        Comment comment=new Comment();
        comment.setArticleid("100000");
        comment.setContent("測試添加的數據");
        comment.setCreatedatetime(LocalDateTime.now());
        comment.setUserid("1003");
        comment.setNickname("凱撒大帝");
        comment.setState("1");
        comment.setLikenum(0);
        comment.setReplynum(0);
        commentService.saveComment(comment);
    }
    /**
     * 查詢所有數據
     */
    @Test
    public void testFindAll(){
        List<Comment> list = commentService.findCommentList();
        System.out.println(list);
    }
    /**
     * 測試根據id查詢
     */
    @Test
    public void testFindCommentById(){
        Comment comment = commentService.findCommentById("62976eb31629ea868c6b5511");
        System.out.println(comment);
    }
}

8. 根據上級ID查詢分頁列表

這個地方非常特殊,必須要在實體類內設置parentid屬性,然后跟據該屬性進行分頁。

① CommentRepository新增方法定義

//根據父id,查詢子評論的分頁列表
Page<Comment> findByParentid(String parentid, Pageable pageable);

② CommentService新增方法

public Page<Comment> findCommentListPageByParentid(String parentid, int page, int size);
/**
* 根據父id查詢分頁列表
* @param parentid
* @param page
* @param size
* @return
*/
public Page<Comment> findCommentListPageByParentid(String parentid, int page, int size){
    return commentRepository.findByParentid(parentid, PageRequest.of(page - 1, size));
}

③ 測試

注意:一定要先插入帶有parentid屬性的數據再測試

/**
 * 測試根據父id查詢子評論的分頁列表
 */
@Test
public void testFindCommentListPageByParentid(){
    Page<Comment> pageResponse = commentService.findCommentListPageByParentid("3", 1, 2);
    System.out.println("----總記錄數:"+pageResponse.getTotalElements());
    System.out.println("----當前頁數據:"+pageResponse.getContent());
}

9. MongoTemplate

繼承MongoRepository接口默認只能實現基礎的查詢、插入、保存操作,而且較為復雜的操作需要先查詢、再設置新數據、最后保存,效率較低。

/**
* 點贊-效率低
* @param id
*/
public void updateCommentThumbupToIncrementingOld(String id){
    Comment comment = CommentRepository.findById(id).get();
    comment.setLikenum(comment.getLikenum() + 1);
    CommentRepository.save(comment);
}

使用MongoTemplate類可以提升此效率,用法如下:

public void updateCommentLikenum(String id);


//注入MongoTemplate
@Autowired
private MongoTemplate mongoTemplate;

/**
 * 點贊數+1
 *
 * @param id
 */
@Override
public void updateCommentLikenum(String id) {
    //查詢對象
    Query query = Query.query(Criteria.where("_id").is(id));
    //更新對象
    Update update = new Update();
    //局部更新,相當于$set
    // update.set(key,value)
    //遞增$inc
    // update.inc("likenum",1);
    update.inc("likenum");
    //參數1:查詢對象
    //參數2:更新對象
    //參數3:集合的名字或實體類的類型Comment.class
    mongoTemplate.updateFirst(query, update, "collection_test");
}

測試:

/**
* 點贊數+1
*/
@Test
public void testUpdateCommentLikenum(){
    //對3號文檔的點贊數+1
    commentService.updateCommentLikenum("62976eb31629ea868c6b5511");
}

三、結尾

以上即為MongoDB-基礎使用(二)的全部內容,感謝閱讀。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容