Spring Repository解析---以Mongo Repository为例

摘要

Spring 为java web 开发领域提供了大量的优秀的框架,第三方包,大大解放了生产力,本文主要介绍Spring Repository在连接数据库这边做的一些封装,并以Mongo Repository为例,详细阐述下Repository实现机制,本文基于spring-data-mongo1.10.4

问题

在使用Repository的时候,相信很多人都有下面的疑问,本文就是致力于解决这些疑惑

  1. Repository 做了什么,和Template有什么区别,两者如何用
  2. Repository是如何做到写个方法名,就可以了(没有查询条件)
  3. Repository什么时候检查方法名的
  4. Repository可不可以只返回部分值,支持返回Long,String等类型吗
  5. 如何去查看实际发送给DB的语句

Repository 实现

一个根据userId找帖子的Repository方法

@Repository
public interface PostRepository extends MongoRepository<Post,String> {
    Post findTopByUserId(Long userId);
}

Bean 加载

这里写图片描述

这里写图片描述

  1. MongoRepositoryFactoryBean 继承自RepositoryFactoryBeanSupport抽象类,提供了一个afterProperties()方法,在属性被加载后,进行RepositoryFactorySupport实例的设置
  2. RepositoryFactoryBeanSupport 抽象类中的afterProperties()方法中调用initAndReturn(),然后调用getRepository()设置定义注解为@Repository的代理类。在这里即设置了PostRepository 的代理类,同时构建了Repository接口的方法,并做校验

    ResolveQuery方法中构建了PartTreeMongoQuery其中类的成员变量PartTree存储了Repository的方法,这样不用每次实际去调用方法.PartTree 将方法通过parser转换成一个主谓语,by前后语义

     private final PartTree.Subject subject;
     private final PartTree.Predicate predicate;

方法调用

  1. 实际调用方法时,在上面Bean加载时代理了QueryExecutorMethodInterceptor

     result.addAdvice(new RepositoryFactorySupport.QueryExecutorMethodInterceptor(information, customImplementation, target));
  2. 所以调用时,就会执行QueryExecutorMethodInterceptor.invoke方法

     private Object doInvoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                Object[] arguments = invocation.getArguments();
                Method actualMethod;
                if(this.isCustomMethodInvocation(invocation)) {
                    actualMethod = this.repositoryInformation.getTargetClassMethod(method);
                    return this.executeMethodOn(this.customImplementation, actualMethod, arguments);
                } else if(this.hasQueryFor(method)) {
                    // 执行RepositoryQuery.execute方法
                    return ((RepositoryQuery)this.queries.get(method)).execute(arguments);
                } else {
                    actualMethod = this.repositoryInformation.getTargetClassMethod(method);
                    return this.executeMethodOn(this.target, actualMethod, arguments);
                }
            }
  3. 调用AbstractMongoQuery.execute去执行query
    这个方法里包含了构建query 条件,发送查询语句的所有逻辑

     public Object execute(Object[] parameters) {
            MongoParameterAccessor accessor = new MongoParametersParameterAccessor(this.method, parameters);
            // 构建query条件
            Query query = this.createQuery(new ConvertingParameterAccessor(this.operations.getConverter(), accessor));
            this.applyQueryMetaAttributesWhenPresent(query);
            ResultProcessor processor = this.method.getResultProcessor().withDynamicProjection(accessor);
            String collection = this.method.getEntityInformation().getCollectionName();
            MongoQueryExecution execution = this.getExecution(query, accessor, new ResultProcessingConverter(processor, this.operations, this.instantiators));
            return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
        }

    PartTreeMongoQuery.createQuery根据之前的partTree结构将查询进行数值绑定,构成真正的query。
    然后执行时调用的是MongoTemplate

    这里写图片描述

答案

  1. Repository 做了什么,和Template有什么区别,两者如何用
    答: Repository是在Template上面进行了进一步的封装,减少了重复代码。
    多数据源的时候需要为repository指定具体的template

  2. Repository是如何做到写个方法名,就可以了(没有查询条件)
    答: 构建一个语义化的parse

  3. Repository什么时候检查方法名的
    答:Bean加载的时候

  4. Repository可不可以只返回部分值,支持返回Long,String等类型吗
    答: 不可以返回部分值,PartTree没有解析返回值语句

  5. 如何去查看实际发送给DB的语句
    org.springframework.data.mongodb.repository.query.MongoQueryCreator 类进行了正在的实际的query语句生成

    当然一个粗暴的方法就是开启org.springframework的debug日志

欢迎关注我的个人公众号,第一时间了解NoSQL技术
这里写图片描述

猜你喜欢

转载自blog.csdn.net/fs1360472174/article/details/78945116