本篇博文開始講解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;
}