记一次mongoDB的collectionName设置的探索

346 阅读3分钟

直接开门见山,大多数会用mongoDB开发的都知道实体类上加上@Document(collection = "XXX")可以直接将操作指定到对应的XXX 集合中,但是对应第一次玩mongoDB的我却浑然不知(留下没有技术的眼泪)。起因是因为每次调用都需要指定collectionName,如下的“refund_error”跟“operator_billing_summary”

Long totalCount = mongoOperations.count(query, reqDTO.getClass(),"refund_error"); 
List list = mongoOperations.find(query, OperatorBillingSummaryPO.class,"operator_billing_summary");

如果不指定collectionName则用默认的类名(等下会讲到源码)

对于处女座的我而言看着特别不舒服,很想把他们全部规整化,于是便有了以下的探索

1、方案一

首先我第一个想到的是将MongoOperations(或者MongoTemplate)在业务代码上所用到

操作(新增修改查询那些)全部用统一MongoDBUtil工具类封装,每次都通过工具类进行操作,collectionName 则通过枚举匹配,或者传入Class类的类名进行分解拆分(这种方法比较土),将RefundErrorInfoBO 转成 refund_error 类似这样,除此之外,也可以用ThreadLocal或者缓存的方式查询,因为思路比较简单就不贴代码了。

2、方案二

由于方案一不合适,直接去spring-data-mongodb的官网搜(docs.spring.io/spring-data…

clipboard.png 于是发现有个生命周期事件,可通过继承 AbstractMongoEventListener 事件监听器进行事件监听,不过只能拿到event事件的实体source、事件处理后的Document以及我朝思暮想的collectionName,但并不能设置具体值,MongoMappingEvent 类如下

public class MongoMappingEvent extends ApplicationEvent { 
    private static final long serialVersionUID = 1L;
    private final @Nullable Document document; 
    private final @Nullable String collectionName; 

    /** 
    * Creates new {@link MongoMappingEvent}. 
    * * @param source must not be {@literal null}.
    * @param document can be {@literal null}. 
    * @param collectionName can be {@literal null}. 
    */ 
    public MongoMappingEvent(T source, @Nullable Document document, @Nullable String collectionName) {
        super(source); 
        this.document = document; 
        this.collectionName = collectionName; } 
        
        /** 
        * @return {@literal null} if not set. 
        */ 
        public @Nullable Document getDocument() {
            return document; 
            } 
            
        /** 
        * Get the collection the event refers to. 
        * 
        * @return {@literal null} if not set. 
        * @since 1.8 
        */ 
        public @Nullable String getCollectionName() {
            return collectionName; 
           } 
           
         /* 
         * (non-Javadoc) 
         * @see java.util.EventObject#getSource() 
         */ 
         @SuppressWarnings({ "unchecked" }) 
         @Override 
         public T getSource() { return (T) super.getSource(); } 
   }

3、方案三

最直接的方法,也就是看源码

首先从find方法入手

org.springframework.data.mongodb.core.MongoTemplate#find(org.springframework.data.mongodb.core.query.Query, java.lang.Class)

8.png 进入设置方法

org.springframework.data.mongodb.core.MongoTemplate#determineCollectionName

1.png 获取PersistentEntity

org.springframework.data.mapping.context.MappingContext#getRequiredPersistentEntity(java.lang.Class<?>)

从Debug模式进入可以看到已经获取到collection了(此时已添加@Document(collection = "refund_error")注解)

2.png

于是返回上层的设置方法

org.springframework.data.mapping.context.AbstractMappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation<?>)

entity不为空,在此退出,可以看到entity是通过AbstractMappingContext内置的HashMap常量persistentEntities获取到的

3.png

persistentEntities是怎么设置进来的呢?点击可以看到调用的put方法,在此方法下

org.springframework.data.mapping.context.AbstractMappingContext#addPersistentEntity(org.springframework.data.util.TypeInformation<?>)

4.png

entity还是被设置了,继续往上看createPersistentEntity方法

createPersistentEntity有三个实现类,看MongoMappingContext的方法

org.springframework.data.mongodb.core.mapping.MongoMappingContext#createPersistentEntity ,此时还是被设置

5.png

继续往上,终于找到了~

6.png 此方法的意思是使用给定的 TypeInformation 创建一个新的 BasicMongoPersistentEntity。如果@Document设置了collection则用设置的值,没有则将集合名称设为实体类型名,实体名则用org.springframework.data.mongodb.MongoCollectionUtils#getPreferredCollectionName获取,并将首字母大写字母转为小写。

至于org.springframework.data.mapping.context.AbstractMappingContext#addPersistentEntity(java.lang.Class<?>)什么时候调用的,则是在SpringAOP初始化bean时,会调用初始化方法afterPropertiesSet

7.png

进而进入org.springframework.data.mapping.context.AbstractMappingContext#initialize,再调用org.springframework.data.mapping.context.AbstractMappingContext#addPersistentEntity(java.lang.Class<?>)实现整个属性的设置

思路比较简单,有疏漏的望各位大佬指点,小弟先告退了

fdaf84ea603fb97c0e098b6890558495.gif