欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MongoDB学习—添加文档

程序员文章站 2022-03-28 14:29:00
...

本篇博文开始讲解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()和该方法差不多,就不在演示了: 
MongoDB学习—添加文档

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

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

MongoDB Compass

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

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

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

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

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

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:aaa@qq.com: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个insert方法,其中那个四个是插入单个文档的,有四个是插入多个文档的。其中传入的参数不同,其中设计到几个参数:

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

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

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

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

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

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

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

从上面的代码看出,代码比较繁琐,需要每次都要获取连接,就好像以前的关系型数据库的操作一样,而且插入的数据必须是标准的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学习—添加文档

从上图可以看出,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学习—添加文档

注:如果实体类类型的数据含有“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;
    }
相关标签: Mongodb