MongoDB學習—添加文檔

本篇博文開始講解MongoDB的操作內容。

首先先講一下MongoDB的添加文檔操作,在本篇博文中,將會從shell、js腳本、MongoDB Compass、java原生驅動、spring封裝幾個方面來講解如何插入MongoDB文檔

MongoDB shell

從前面的方法彙總的集合方法中,我們可以看到shell提供了三個插入方法:

  • db.collection.insert() : 插入一個或多個指定文檔

    其語法爲:

db.collection.insert(
   <document or array of documents>,
   {
     writeConcern: <document>,
     ordered: <boolean>
   }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • db.collection.insertOne() :插入一個指定文檔

其語法爲:

db.collection.insertOne(
   <document>,
   {
      writeConcern: <document>
   }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • db.collection.insertMany() :插入多個指定文檔

其語法爲:

db.collection.insertMany(
   [ <document 1> , <document 2>, ... ],
   {
      writeConcern: <document>,
      ordered: <boolean>
   }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面的方法主要有三個參數:

document – 該參數指要插入的一個或多個文檔的數據,如果是一個文檔,則該參數爲一個json數據,若是多個文檔,則該參數是多個json數據組成的數組。

writeConcern – 可選參數,該參數指的是該次的插入操作的寫入關注程度,其值爲一個文檔,表現爲json數據。大家可以參考前面的寫入關注的講解。

ordered – 2.6版本以後的新參數,可選參數。如果值爲true,則將數組中文檔的進行有序插入,如果一個文檔發生錯誤,mongodb將返回,而無需處理數組中的剩餘文檔。如果false,執行無序插入,如果錯誤發生在某個文檔中,則繼續處理數組中的剩餘文檔。默認爲true。

注: 
1.上面的三個方法是集合(collection)的方法,所以我們需要有對應的集合,當然如果我們執行方法時,指定的集合並不存在,MongoDB會自動創建該集合,並插入數據。 
2.如果文檔沒有指定”_id”字段,那麼mongodb將在插入之前爲文檔分配一個惟一的objectid,並將該objectId作爲”_id”字段插入文檔中。如果文檔包含”_id”字段,則_id值必須是集合中唯一的,以避免重複的密鑰錯誤。

比如下圖所示,我用幾個insert方法演示插入數據。由於insertOne()和insertMany()和該方法差不多,就不在演示了: 
這裏寫圖片描述

上圖中從只插入一條數據、插入一條數據+寫入關注、插入多條數據+寫入關注+是否排序等幾個方面演示。 
其中上面的mongoTest集合原先並不存在,我們可以直接調用db.mongoTest.insert(...)來默認創建並插入數據。

在shell中我們使用insert()方法插入文檔後,不關錯誤 還是失敗,都會返回一個BulkWriteResult()對象,內部描述了插入操作的結果。

從上面的插入的數據可以看出,在同一個集合中插入數據,我們不必遵循每個文檔必須具有相同的字段名,且就算相同的字段名在不同的文檔中其值可以爲不同類型。在這點上MongoDB比關係型數據庫更加靈活。當然建議大家還是將同一類型的文檔放在一個集合中,不同類型的文檔放在不同的集合中。

js腳本 
我們除了可以在shell中插入數據,我們還可以在一個js腳本文件中插入數據。我們可以首先新建一個腳本文件,然後在文件中使用JavaScript語法和shell提供的方法,來執行js腳本來實現操作數據庫。

比如下面的語句:

//獲取數據庫鏈接
var db = connect("localhost/words","yfl","yfl");
//自定義文檔
var document1 = {key:"mongo_js",value:"測試js腳本插入數據"};
//調用集合mongoTest的insert方法
db.mongoTest.insert(document1);

var document2 = {key:"mongo_js2",value:"測試js腳本插入數據,附帶參數"};
//自定義帶有寫入關注和是否順序插入參數
var options = {writeConcern: { w: "majority", wtimeout: 5000 },ordered: false }
//帶參數插入數據
db.mongoTest.insert(document2,options);

//自定義多個文檔
var document3 = {key:"mongo_js3",value:"測試js腳本批量插入數據"};
var document4 = {key:"mongo_js4",value:"測試js腳本批量插入數據"};
//自定義一個文檔數組,用於存放文檔
var documentArray = new Array();
//調用數組的push方法將文檔添加進數組
documentArray.push(document3);
documentArray.push(document4);
//將數組傳入insert方法實現批量插入
db.mongoTest.insert(documentArray);
//獲取集合對象
var mongoTest = db.getCollection("mongoTest");
//查詢“key”這個鍵的值在"mongo_js","mongo_js4","mongo_js2","mongo_js3"之中的記錄
var cursor = mongoTest.find({key:{$in:["mongo_js","mongo_js4","mongo_js2","mongo_js3"]}});
//打印出查詢的數據
printjson(cursor.toArray());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

上面的代碼就是js中插入的數據的代碼,從上面的代碼,我們可以看到在js文件中我們既可以使用JavaScript語法又可以使用shell方法。相比較在shell中操作數據庫相比,js腳本來操作數據庫能夠更加靈活,其易修改、易保存記錄、可以定時執行等等優點。

執行js腳本只需要在cmd.exe中切換到mongoDB的安裝目錄的bin目錄下,直接執行mongo js腳本路徑即可,如下圖: 
這裏寫圖片描述

上面插入結果我們可以使用MongoDB Compass查看,如下圖: 
這裏寫圖片描述

MongoDB Compass

上面的練習我們使用了shell插入了5條數據,這時候我們可以使用MongoDB Compass來查看先前的數據,如圖: 
這裏寫圖片描述
我們可以可以看到上面操作插入的數據在這裏都能看到。

下面我們開始演示如何在MongoDB Compass中插入數據:

首先我們點擊左上面的綠色的“INSERT DOCUMENT”按鈕,如圖: 
這裏寫圖片描述

然後輸入我們要插入的數據,如圖: 
這裏寫圖片描述

點擊”INSERT”或”CANCEL”來確認插入或取消插入操作,確認之後,我們可以看到剛纔插入的數據: 
這裏寫圖片描述

MongoDB Compass插入數據非常簡單,每次只能插入一條。

MongoDB java Driver

接下來講解的是如何使用MongoDB的java驅動來操作MongoDB,具體的準備和介紹大家可以看學習筆記(十)。

大家看下面的代碼:

    /**
     * 用於演示MongoDB原生驅動添加數據
     */
    public void addDataDemo1(){
        // 獲取MongoClient對象
        MongoClient client = null;
       // 直接使用默認host和port(該方法不能進行數據庫授權認證)
//        client = new MongoClient();

       // 使用指定host和端口號,(該方法不能進行數據庫授權認證)
//        client = new MongoClient("localhost",27017);

        // 使用封裝的ServerAddress鏈接對象,無需授權時
//        client = new MongoClient(new ServerAddress("localhost",27017));
        // 使用封裝的ServerAddress鏈接對象,需授權時,使用MongoCredential對象封裝授權信息createCredential(String userName, String database, char[] password),
        // MongoDB 的java驅動的文檔中的授權說是可以缺少 MongoClientOptions的,但是在其api文檔中並沒有提供只有 ServerAddress、MongoCredential的構造方法,估計是他們文檔更新不及時,這裏就直接加上 MongoClientOptions參數。
//        client = new MongoClient(new ServerAddress("localhost",27017), MongoCredential.createCredential("yfl","words","yfl".toCharArray()),MongoClientOptions.builder().build());

       // 使用URI鏈接MongoDB,MonoClientURI格式爲:mongodb://[用戶名:密碼@]host:port[/數據庫],強烈建議使用該種身份驗證
        client = new MongoClient(new MongoClientURI("mongodb://yfl:yfl@localhost:27017/words"));

        //獲取MongoDatabase 對象
        MongoDatabase database = client.getDatabase("words");
        //獲取集合對象
        MongoCollection col = database.getCollection("mongoTest");

        //創建將要插入的文檔對象,插入的文檔類型必須是Document類型
        Document document = new Document("javakey","sss").append("value",5);
        col.insertOne(document);

        Document document1 = new Document("javakey","策劃").append("value",5);
        Document document2 = new Document("javakey","sdsd").append("value",10);
        List list = new ArrayList();
        list.add(document1);
        list.add(document2);
        col.insertMany(list);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

首先講解了我們如何在java程序中獲取MongoClient 對象,在程序中,MongoDB有可能沒有開啓權限認證,也有可能開啓了權限認證,當沒開權限認證時,我們可以看到獲取MongoClient 對象的方法都快可以。

但是開啓權限認證之後,只有兩種方法能夠獲取權限並獲取到MongoClient對象,在以前的版本可以先獲取MongoClient對象,然後獲取對應的數據庫,並調用數據庫的授權方法來授權,但是現在的新版本好像取消掉了該種認證方式,認證只能在獲取MongoClient對象的時候認證。

在獲取MongoClient身份權限認證有兩種方式:

1.是使用MongoClient的構造方法時,傳入ServerAddress+MongoCredential+MongoClientOptions三個參數。ServerAddress參數是描述要鏈接數據庫的host和port。MongoCredential是描述要連接數據庫的身份驗證信息,有用戶名、密碼、要鏈接的數據庫名。MongoClientOptions描述各種設置來控制MongoClient的行爲。

2.使用MongoClientURI對象來描述要鏈接數據庫的信息以及權限信息,然後在構造MongoClient對象時傳入該MongoClientURI對象。

當然出了以上演示的構造方法,MongoClient還提供了更多的構造方法,大家可以參考上篇的MongoClient對象的方法列表以及官方文檔。

獲取到MongoClient對象之後,我們就可以獲取到對應的MongoDatabase 、MongoCollection 對象,獲取到集合對象(MongoCollection )之後,我們就可以調用其提供的insert方法,如圖: 
這裏寫圖片描述

其提供了8個insert方法,其中那個四個是插入單個文檔的,有四個是插入多個文檔的。其中傳入的參數不同,其中設計到幾個參數:

Object tDocument – 該參數就是要插入的數據庫文檔數據,其在官方API文檔提供的方法的類型是TDocument,但是MongoDB並沒有提供TDocument對象,在這例TDocument只是說明該參數是一個文檔類型。具體的我們在插入的時候應該是使用Document對象,該對應在以前的版本里是使用BasicDBObject,但是新版本不在支持該種類型作爲文檔數據。在插入數據時,如果文檔數據的類型不對,會報*** cannot be cast to org.bson.Document

List – 該參數表示List<Document>,表示要插入文檔的列表。

ClientSession – 與此操作關聯的客戶端會話(session)

InsertOneOptions – 應用於將單個文檔插入到集合中的操作的選項。

InsertManyOptions – 應用於將多個文檔插入到集合中的操作的選項。

上面的大家都可以查看對應的官方文檔。

所以上面的代碼我只是插入了一個單個文檔,一個多文檔插入。結果爲: 
這裏寫圖片描述

從上面的代碼看出,代碼比較繁瑣,需要每次都要獲取連接,就好像以前的關係型數據庫的操作一樣,而且插入的數據必須是標準的Document對象,這樣如果文檔中嵌套文檔,就非常麻煩,而且有時候,我們存儲的數據不能確定其key值,有時候我們想將一個無論什麼類型的數據想直接存放進去,就非常麻煩了。所以spring爲了解決這一問題,其對驅動進行了封裝。爲spring-data-mongodb。

mongoTemplate

該對象是spring封裝MongoDB的java驅動爲spring-data-mongodb暴露出來的一個對象,在實際應用中,我們只需要進行相應的配置即可,至於如何配置,請看前面的博文,這裏就不介紹了。

mongoTemplate的應用非常簡單,只需要在使用的地方注入MongoTemplate對象即可,比如:

    @Autowired
    private MongoTemplate mongoTemplate;
  • 1
  • 2
  • 3

由於數據庫的鏈接什麼的在配置文件中都配置好了,所以這裏我們就不需要在麻煩的獲取各種對象,我們只需要直接操作mongoTemplate的insert方法即可: 
這裏寫圖片描述

從上圖可以看出,mongoTemplate提供給了我們6個insert方法,分別3個批量插入,3個單個插入。 
其主要參數只有三個: 
Collection《? extends Object》/ Object batchToSave :該參數就是我們要插入的數據,其類型爲繼承Object的類 ,也就是說只要是繼承自Object的所有數據類型,我們都可以通過該方法存儲,封裝類會自己幫我們轉換數據類型。

collectionName : 我們將要存儲數據到哪個集合,若沒有該參數,mongoTemplate會自動存儲到默認的集合中。

Class entityClass :實體類的class,mongoTemplate使用該實體類class來確定將要存儲數據庫的集合。

注:至於mongoTemplate當我們不傳collectionName或傳 entityClass是如何確定存儲的集合的, 
(經實際測試,存儲的默認集合名爲要存儲數據的類型所屬類型,比如com.alibaba.fastjson.JSONObject存儲到的是jSONObject集合中,com.mongo.mongodb.Business.entity.MongoAddDemo實體類中存儲的是mongoAddDemo”集合,也就是類class的首字母小寫作爲集合名稱。但是HashMap類型的數據不能存儲戶,會報錯)

如果大家有興趣研究mongoTemplate是如何來確定默認集合名稱的可以看最下面的源代碼(分析的很亂,很雜),下面的是使用mongoTemplate操作添加數據的方法。

package com.mongo.mongodb.Business;

import com.alibaba.fastjson.JSONObject;
import com.mongo.mongodb.Business.entity.MongoAddDemo;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class MongodbTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 使用spring封裝mongodb的對象mongoTemplate添加數據
     */
    public void addDataDemo2(){

        //因爲如果要是用mongoTemplate對象來操作MongoDB,我們首先需要配置要鏈接的數據庫,所以我們就不在需要獲取各種對象了,
        //只需要將mongoTemplate注入進來,然後操作insert方法
        JSONObject json = new JSONObject();
        json.put("key","json");
        json.put("value","測試存放json");
        mongoTemplate.insert(json,"mongoTest");//添加json數據

        Map map = new HashMap();
        map.put("key","map");
        map.put("value","測試存放map");
        mongoTemplate.insert(map,"mongoTest");//添加map數據

        MongoAddDemo entity = new MongoAddDemo();
        entity.setKey("entity");
        entity.setValue("測試存放entity");
        mongoTemplate.insert(entity,"mongoTest");//添加entity數據

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

結果如圖: 
這裏寫圖片描述

注:如果實體類類型的數據含有“id”字段,則存儲時,mongoTemplate會自動轉換成mongo的”_id”字段。 
——————————————————————————————————————————————–

獲取默認集合名稱源代碼:

    //1.MongoTemplate類
    //首先根據根據傳入的文檔數據調用determineEntityCollectionName方法確定集合名稱
    public void insert(Object objectToSave) {
        Assert.notNull(objectToSave, "ObjectToSave must not be null!");
        this.ensureNotIterable(objectToSave);
        this.insert(objectToSave, this.determineEntityCollectionName(objectToSave));
    }

    //2.MongoTemplate類
    //對傳進的參數進行判空,若參數不爲空,則使用傳入的文檔數據對象調用getClass()獲取該對象的class並作爲參數調用determineCollectionName方法,來確定集合名稱
    private <T> String determineEntityCollectionName(@Nullable T obj) {
        return null != obj ? this.determineCollectionName(obj.getClass()) : null;
    }

    //3.MongoTemplate類
    //首先對傳入的class判空,然後調用mappingContext類的getRequiredPersistentEntity()方法,並將返回值強轉爲MongoPersistentEntity對象,
    //然後獲取到MongoPersistentEntity對象的getCollection()獲取集合名稱。從這裏可以看出關鍵在於mappingContext.getRequiredPersistentEntity(entityClass)方法
    String determineCollectionName(@Nullable Class<?> entityClass) {
        if (entityClass == null) {
            throw new InvalidDataAccessApiUsageException("No class parameter provided, entity collection can't be determined!");
        } else {
        //如果大家對getRequiredPersistentEntity方法是如何將class對象轉換成MongoPersistentEntity對象可以看下面的源碼。
            return ((MongoPersistentEntity)this.mappingContext.getRequiredPersistentEntity(entityClass)).getCollection();
        }
    }

    //4.MongoPersistentEntity類
    //該類是一個接口類,其提供了四個接口方法,其中getCollection()方法用於提供獲取集合名稱的方法
    public interface MongoPersistentEntity<T> extends PersistentEntity<T, MongoPersistentProperty> {
        String getCollection();

        String getLanguage();

        @Nullable
        MongoPersistentProperty getTextScoreProperty();

        boolean hasTextScoreProperty();
}

    //5.BasicMongoPersistentEntity類,其實現了MongoPersistentEntity接口類,提供了用於獲取集合名稱的方法。其判斷當前BasicMongoPersistentEntity對象的expression參數是否存在,若不存在,返回當前的collection參數,若存在則返回其getValue()的值
    public String getCollection() {
        return this.expression == null ? this.collection : (String)this.expression.getValue(this.context, String.class);
    }

    //6.我們看下BasicMongoPersistentEntity的構造方法
    public BasicMongoPersistentEntity(TypeInformation<T> typeInformation) {
        super(typeInformation, BasicMongoPersistentEntity.MongoPersistentPropertyComparator.INSTANCE);
        Class<?> rawType = typeInformation.getType();
        String fallback = MongoCollectionUtils.getPreferredCollectionName(rawType);
        this.context = new StandardEvaluationContext();
        if (this.isAnnotationPresent(Document.class)) {
            Document document = (Document)this.getRequiredAnnotation(Document.class);
            this.collection = StringUtils.hasText(document.collection()) ? document.collection() : fallback;
            this.language = StringUtils.hasText(document.language()) ? document.language() : "";
            this.expression = detectExpression(document);
        } else {
            this.collection = fallback;
            this.language = "";
            this.expression = null;
        }

    }
    //7我們主要看上面的fallback參數的生成方式
    public static String getPreferredCollectionName(Class<?> entityClass) {
        return StringUtils.uncapitalize(entityClass.getSimpleName());//傳入class的名稱計算出集合名稱
    }

    public static String uncapitalize(String str) {
        return changeFirstCharacterCase(str, false);//設置第一個字符爲小寫
    }
    //根據傳入字符串以及第二個元素確定字符串首字母大寫還是小寫
    private static String changeFirstCharacterCase(String str, boolean capitalize) {
        if (!hasLength(str)) {
            return str;
        } else {
            char baseChar = str.charAt(0);
            char updatedChar;
            if (capitalize) {
                updatedChar = Character.toUpperCase(baseChar);
            } else {
                updatedChar = Character.toLowerCase(baseChar);
            }

            if (baseChar == updatedChar) {
                return str;
            } else {
                char[] chars = str.toCharArray();
                chars[0] = updatedChar;
                return new String(chars, 0, chars.length);
            }
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

getRequiredPersistentEntity():


    //1.MappingContext類
    //該方法是MappingContext接口類的一個方法。其調用自身提供的getPersistentEntity接口,
    //將傳入的接口轉換成繼承PersistentEntity的類E(`E extends PersistentEntity<?, P>`)
    default E getRequiredPersistentEntity(Class<?> type) throws MappingException {
        E entity = this.getPersistentEntity(type);
        if (entity != null) {
            return entity;
        } else {
            throw new MappingException(String.format("Couldn't find PersistentEntity for type %s!", type));
        }
    }

    //2.MappingContext類
    //MappingContext提供的接口類,用於將傳入的類class轉換成繼承PersistentEntity的類E(`E extends PersistentEntity<?, P>`)
    E getPersistentEntity(Class<?> var1);

    //3.AbstractMappingContext類
    //AbstractMappingContext是MappingContext實現類。實現了上面的getPersistentEntity()接口。
    //其首先將class作爲參數調用ClassTypeInformation的靜態方法,將返回值強轉成TypeInformation接口類,然後調用getPersistentEntity()方法
    //ClassTypeInformation繼承TypeDiscoverer類,TypeDiscoverer實現TypeInformation接口類,所以下面的強轉關係成立
    public E getPersistentEntity(Class<?> type) {
        return this.getPersistentEntity((TypeInformation)ClassTypeInformation.from(type));//若想查看from方法功能看下一段源碼
    }

    //4.AbstractMappingContext類
    //獲取當前類維持的persistentEntities 這個map中存儲的key爲type對應的MutablePersistentEntity對象值,
    //若persistentEntities不包含key爲type的,則判斷當前type是否應該寫入到persistentEntities中,
    //若不應該寫入,則persistentEntities 中維持一個key爲type,value爲空的Optional對象的數據。
    //若允許寫入,則計算對應的Optional對象值,寫入並返回強制轉換成MutablePersistentEntity對象
    private final Map<TypeInformation<?>, Optional<E>> persistentEntities = new HashMap();//當前類的用於存儲常出現的實體類的map,key是TypeInformation對象

     public E getPersistentEntity(TypeInformation<?> type) {
        Assert.notNull(type, "Type must not be null!");//判空

        try {
            this.read.lock();//鎖定讀取鎖
            Optional<E> entity = (Optional)this.persistentEntities.get(type);//從維持的persistentEntities獲取保存當前TypeInformation對象的對應的值,
            if (entity != null) {
                MutablePersistentEntity var3 = (MutablePersistentEntity)entity.orElse((Object)null);
                return var3;//如果維持的persistentEntities這個map中有當前傳入文檔的class這個key,獲取其value值,使用orElse對value進行判空處理後,強轉成MutablePersistentEntity對象。
            }
        } finally {
            this.read.unlock();//釋放讀取鎖
        }

        if (!this.shouldCreatePersistentEntityFor(type)) {//判斷當前的type是否應該寫入到維持的persistentEntities中,如果不,則將persistentEntities維持一個key爲type,值爲空的Optional對象的數據
            try {
                this.write.lock();//鎖定寫入鎖
                this.persistentEntities.put(type, this.NONE);
            } finally {
                this.write.unlock();//釋放寫入鎖
            }

            return null;
        } else if (this.strict) {
            throw new MappingException("Unknown persistent entity " + type);
        } else {
            return (MutablePersistentEntity)this.addPersistentEntity(type).orElse((Object)null);//如果當前type應該寫入到維持的persistentEntities這個map中,則寫入,並返回當前type所對應的value值,並強轉成MutablePersistentEntity對象。
        }
    }

    //5.AbstractMappingContext
    //給維持的persistentEntities這個map中添加一個key爲TypeInformation對象的記錄
    protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
        Assert.notNull(typeInformation, "TypeInformation must not be null!");//判空

        Optional var3;
        label151: {
            try {
                this.read.lock();//鎖定讀取鎖
                Optional<E> persistentEntity = (Optional)this.persistentEntities.get(typeInformation);//判斷維持的persistentEntities這個map中是否包含key爲typeInformation的值,如果包含,則直接返回,若不包含則繼續執行下面代碼
                if (persistentEntity == null) {
                    break label151;
                }

                var3 = persistentEntity;
            } finally {
                this.read.unlock();//釋放讀取鎖
            }

            return var3;
        }

        Class type = typeInformation.getType();//獲取typeInformation對象中的type參數(class類型,即爲當初傳入的文檔數據類型的class,比如傳入的是一個map文檔,則爲map.getClass())
        var3 = null;

        MutablePersistentEntity entity;
        try {
            this.write.lock();//鎖定寫入鎖
            //根據type創建一個MutablePersistentEntity對象。
            //創建的是BasicMongoPersistentEntity對象,
            //BasicMongoPersistentEntity對象繼承BasicPersistentEntity對象,
            //BasicPersistentEntity對象實現MutablePersistentEntity對象接口類
            entity = this.createPersistentEntity(typeInformation);
            //將創建的MutablePersistentEntity封裝到Optional對象中,並維護到persistentEntities這個map中
            this.persistentEntities.put(typeInformation, Optional.of(entity));
            PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type);//獲取類的屬性
            Map<String, PropertyDescriptor> descriptors = new HashMap();//創建用於存儲PropertyDescriptor的map
            PropertyDescriptor[] var6 = pds;
            int var7 = pds.length;//獲取屬性數目

            for(int var8 = 0; var8 < var7; ++var8) {//對屬性進行存儲,key爲屬性的name,值爲PropertyDescriptor對象
                PropertyDescriptor descriptor = var6[var8];
                descriptors.put(descriptor.getName(), descriptor);
            }

            try {
                //Invoke the given callback on all fields in the target class, going up the class hierarchy to get all declared fields
                //在給定的目標類(type)的所有字段上請求給定的回調函數(persistentPropertyCreator),向上調整類層次結構以獲取所有聲明的字段
                ReflectionUtils.doWithFields(type, persistentPropertyCreator, AbstractMappingContext.PersistentPropertyFilter.INSTANCE);。

                persistentPropertyCreator.addPropertiesForRemainingDescriptors();
                entity.verify();
                if (this.persistentPropertyAccessorFactory.isSupported(entity)) {
                    entity.setPersistentPropertyAccessorFactory(this.persistentPropertyAccessorFactory);
                }
            } catch (MappingException var19) {
                this.persistentEntities.remove(typeInformation);
                throw var19;
            }
        } catch (BeansException var20) {
            throw new MappingException(var20.getMessage(), var20);
        } finally {
            this.write.unlock();
        }

        if (this.applicationEventPublisher != null && entity != null) {
            this.applicationEventPublisher.publishEvent(new MappingContextEvent(this, entity));
        }

        return Optional.of(entity);
    }   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

form方法:

//1.ClassTypeInformation類
    //其繼承TypeDiscoverer類,TypeDiscoverer實現TypeInformation接口類
    //先判空,然後調用Map的computeIfAbsent()方法,傳入文檔的class和一個用ClassTypeInformation的構造函數創建的ClassTypeInformation對象。
    //並將返回結果強轉成ClassTypeInformation對象。

    private static final Map<Class<?>, ClassTypeInformation<?>> CACHE = new ConcurrentReferenceHashMap();

    public static <S> ClassTypeInformation<S> from(Class<S> type) {
        Assert.notNull(type, "Type must not be null!");
        return (ClassTypeInformation)CACHE.computeIfAbsent(type, (it) -> {
            return new ClassTypeInformation(type);
        });//將文檔的class放入ClassTypeInformation對象中。
    }

    //2.Map類
    //如果指定的鍵尚未與值mappingFunction關聯,則嘗試使用給定的映射函數計算其值,並將其輸入到此映射中。
    //也就是說該方法功能是將key(文檔的class)與傳入的mappingFunction是否有關聯,若沒有關聯,則將其關聯,並返回關聯之後的mappingFunction對象,或新的mappingFunction對象。
    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);//判空
        V v;
        if ((v = get(key)) == null) {//判斷當前調用當前的方法的對象(上面的CACHE這個map)是否包含傳入key(即文檔的class)
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {//將文檔的class運用apply函數(映射函數)
                put(key, newValue);
                return newValue;
            }
        }

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