摘要
Spring 为java web 开发领域提供了大量的优秀的框架,第三方包,大大解放了生产力,本文主要介绍Spring Repository在连接数据库这边做的一些封装,并以Mongo Repository为例,详细阐述下Repository实现机制,本文基于spring-data-mongo1.10.4
问题
在使用Repository的时候,相信很多人都有下面的疑问,本文就是致力于解决这些疑惑
- Repository 做了什么,和Template有什么区别,两者如何用
- Repository是如何做到写个方法名,就可以了(没有查询条件)
- Repository什么时候检查方法名的
- Repository可不可以只返回部分值,支持返回Long,String等类型吗
- 如何去查看实际发送给DB的语句
Repository 实现
一个根据userId找帖子的Repository方法
@Repository
public interface PostRepository extends MongoRepository<Post,String> {
Post findTopByUserId(Long userId);
}
Bean 加载
- MongoRepositoryFactoryBean 继承自RepositoryFactoryBeanSupport抽象类,提供了一个afterProperties()方法,在属性被加载后,进行RepositoryFactorySupport实例的设置
RepositoryFactoryBeanSupport 抽象类中的afterProperties()方法中调用initAndReturn(),然后调用getRepository()设置定义注解为@Repository的代理类。在这里即设置了PostRepository 的代理类,同时构建了Repository接口的方法,并做校验
ResolveQuery方法中构建了PartTreeMongoQuery其中类的成员变量PartTree存储了Repository的方法,这样不用每次实际去调用方法.PartTree 将方法通过parser转换成一个主谓语,by前后语义
private final PartTree.Subject subject; private final PartTree.Predicate predicate;
方法调用
实际调用方法时,在上面Bean加载时代理了QueryExecutorMethodInterceptor
result.addAdvice(new RepositoryFactorySupport.QueryExecutorMethodInterceptor(information, customImplementation, target));
所以调用时,就会执行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); } }
调用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
答案
Repository 做了什么,和Template有什么区别,两者如何用
答: Repository是在Template上面进行了进一步的封装,减少了重复代码。
多数据源的时候需要为repository指定具体的templateRepository是如何做到写个方法名,就可以了(没有查询条件)
答: 构建一个语义化的parseRepository什么时候检查方法名的
答:Bean加载的时候Repository可不可以只返回部分值,支持返回Long,String等类型吗
答: 不可以返回部分值,PartTree没有解析返回值语句如何去查看实际发送给DB的语句
org.springframework.data.mongodb.repository.query.MongoQueryCreator 类进行了正在的实际的query语句生成当然一个粗暴的方法就是开启org.springframework的debug日志
欢迎关注我的个人公众号,第一时间了解NoSQL技术