MongoDB学习—添加文档

本篇博文开始讲解MongoDB的操作内容。

首先先讲一下MongoDB的添加文档操作,在本篇博文中,将会从shell、js脚本、MongoDB Compass、java原生驱动、spring封装几个方面来讲解如何插入MongoDB文档

MongoDB shell

从前面的方法汇总的集合方法中,我们可以看到shell提供了三个插入方法:

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

    其语法为:

db.collection.insert(
   ,
   {
     writeConcern: ,
     ordered: 
   }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • db.collection.insertOne() :插入一个指定文档

其语法为:

db.collection.insertOne(
   ,
   {
      writeConcern: 
   }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • db.collection.insertMany() :插入多个指定文档

其语法为:

db.collection.insertMany(
   [  , , ... ],
   {
      writeConcern: ,
      ordered: 
   }
)
  • 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()和该方法差不多,就不在演示了: 
MongoDB学习—添加文档_第1张图片

上图中从只插入一条数据、插入一条数据+写入关注、插入多条数据+写入关注+是否排序等几个方面演示。 
其中上面的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学习—添加文档_第2张图片

上面插入结果我们可以使用MongoDB Compass查看,如下图: 
MongoDB学习—添加文档_第3张图片

MongoDB Compass

上面的练习我们使用了shell插入了5条数据,这时候我们可以使用MongoDB Compass来查看先前的数据,如图: 
MongoDB学习—添加文档_第4张图片
我们可以可以看到上面操作插入的数据在这里都能看到。

下面我们开始演示如何在MongoDB Compass中插入数据:

首先我们点击左上面的绿色的“INSERT DOCUMENT”按钮,如图: 
MongoDB学习—添加文档_第5张图片

然后输入我们要插入的数据,如图: 
MongoDB学习—添加文档_第6张图片

点击”INSERT”或”CANCEL”来确认插入或取消插入操作,确认之后,我们可以看到刚才插入的数据: 
MongoDB学习—添加文档_第7张图片

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方法,如图: 
MongoDB学习—添加文档_第8张图片

其提供了8个insert方法,其中那个四个是插入单个文档的,有四个是插入多个文档的。其中传入的参数不同,其中设计到几个参数:

Object tDocument – 该参数就是要插入的数据库文档数据,其在官方API文档提供的方法的类型是TDocument,但是MongoDB并没有提供TDocument对象,在这例TDocument只是说明该参数是一个文档类型。具体的我们在插入的时候应该是使用Document对象,该对应在以前的版本里是使用BasicDBObject,但是新版本不在支持该种类型作为文档数据。在插入数据时,如果文档数据的类型不对,会报*** cannot be cast to org.bson.Document

List – 该参数表示List,表示要插入文档的列表。

ClientSession – 与此操作关联的客户端会话(session)

InsertOneOptions – 应用于将单个文档插入到集合中的操作的选项。

InsertManyOptions – 应用于将多个文档插入到集合中的操作的选项。

上面的大家都可以查看对应的官方文档。

所以上面的代码我只是插入了一个单个文档,一个多文档插入。结果为: 
MongoDB学习—添加文档_第9张图片

从上面的代码看出,代码比较繁琐,需要每次都要获取连接,就好像以前的关系型数据库的操作一样,而且插入的数据必须是标准的Document对象,这样如果文档中嵌套文档,就非常麻烦,而且有时候,我们存储的数据不能确定其key值,有时候我们想将一个无论什么类型的数据想直接存放进去,就非常麻烦了。所以spring为了解决这一问题,其对驱动进行了封装。为spring-data-mongodb。

mongoTemplate

该对象是spring封装MongoDB的java驱动为spring-data-mongodb暴露出来的一个对象,在实际应用中,我们只需要进行相应的配置即可,至于如何配置,请看前面的博文,这里就不介绍了。

mongoTemplate的应用非常简单,只需要在使用的地方注入MongoTemplate对象即可,比如:

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

由于数据库的链接什么的在配置文件中都配置好了,所以这里我们就不需要在麻烦的获取各种对象,我们只需要直接操作mongoTemplate的insert方法即可: 
MongoDB学习—添加文档_第10张图片

从上图可以看出,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

结果如图: 
MongoDB学习—添加文档_第11张图片

注:如果实体类类型的数据含有“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  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 extends PersistentEntity {
        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 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`)
    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`)
    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, Optional> persistentEntities = new HashMap();//当前类的用于存储常出现的实体类的map,key是TypeInformation对象

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

        try {
            this.read.lock();//锁定读取锁
            Optional 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 addPersistentEntity(TypeInformation typeInformation) {
        Assert.notNull(typeInformation, "TypeInformation must not be null!");//判空

        Optional var3;
        label151: {
            try {
                this.read.lock();//锁定读取锁
                Optional 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 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, ClassTypeInformation> CACHE = new ConcurrentReferenceHashMap();

    public static  ClassTypeInformation from(Class 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 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;
    }

你可能感兴趣的:(mongodb)