【Spring】Sping Data JPA 深入学习之①查询方法原理探究

基本概念

  • Spring Data JPA是Spring公司开发的Java Persistence API 相对于 sun公司开发的JPA
  • Spring Data JPA 整合了Hibernate,换句话说,Spring Data JPA 的默认实现是用的Hibernate

一个疑问:为什么JPA没有实现类?

在项目中观察到前辈们自定义的repository层接口没有实现类,查阅源码和资料得知,这是因为 我们自定义的接口继承了JpaRepository接口,而此接口有一个自带的实现类SimpleJpaRepository,它实现了部分常用的CRUD持久化方法。
我们用自定义接口对象调用JpaRepository接口中的方法时,在自定义接口中若没有找到重写的方法,便会去调用父类的方法,而父类只有一个自带的实现类SimpleJpaRepository,便会去调用此类的实现。这类型的方法常见的有save、saveAll等。
但是同时我又发现,有一些查询方法单纯靠方法名查询,比如findByNameAndPassword(),第一次见到时感觉自己要失业了,居然只看方法名就知道要怎么查询…于是深入探究总结了一下。

Spring Data JPA的查询方式

  • SimpleJpaRepository实现的自带方法 如save和saveAll等
  • 在继承了JpaRepository自定义接口中按照下图语法编写的方法
    在这里插入图片描述
  • 使用@Query在自定义的接口上编写所需要的SQL语句

三种查询方式的实现原理

  • 第一种如上所述,是由SimpleJpaRepository实现的。那么SimpleJpaRepository是怎么实现的呢?拿save()举例,找一下源代码就可以发现,在运用模板类的基础上,调用了EntityManager类的perisist()方法,如图所示。

在这里插入图片描述

  • 而EntityManager是在javax.persistence包中的内容,应该是原生JPA的内容
  • 再跟踪一下persist(),可以发现persist()的实现是由hibernate中Session接口定义的
    在这里插入图片描述
  • 如图为其实现,看来是调用了hibernate的内容,hibernate具体内容我还不是很熟练,留待将来回顾。
    在这里插入图片描述

  • 第二种方法是最让我摸不到头脑的,没有实现类,单靠方法名就可以实现查询。经过源码的阅读和资料的查阅,我发现它的实现是通过拦截器实现的。在真正的方法体调用之前,使用拦截器获取当前方法,并解析其方法名,实现根据方法名执行查询。在spring-data-commons-xxxx-RELEASE-sources.jar中,springframework–>data–>repository–>core–>support包里的RepositoryFactorySupport类,如图所示。
    在这里插入图片描述

  • 重点关注注释中:在创建接口时,此类对Repository接口创建实现了配置的代理,然后将控制权交给QueryExecuterMethodInterceptor,让拦截器根据查询策略QueryLookupStrategy.Key解析其内部是否有需要配置的查询方法。

  • QueryExecuterMethodInterceptor就是上文提到的拦截器,是实现了MethodInterceptor接口的RepositoryFactorySupport的一个内部类,如图所示,其中的queries属性将拦截的自定义方法Method类和查询RepositoryQuery利用Map关联起来。
    在这里插入图片描述

  • 接下来到了拨云见日的时刻了,我们跟踪RepositoryQuery,发现其是一个“爷爷”级别的接口,类似Repository接口,其中只有两个方法,如图所示,execute()和getQueryMethod()。
    在这里插入图片描述

  • 只有这么两个方法是一定完不成各种具体的复杂工作的,它的实现类必定各种各样。经过资料的查阅和来回来去一顿乱翻,我找到了它的实现类中的PartTreeJpaQuery。
    在这里插入图片描述

  • 在PartTreeJpaQuery里,有一条属性叫做tree,类型是PartTree,如图所示。PartTreeJpaQuery的构造函数中,调用了PartTree的构造函数,传入了方法名称,在PartTree的构造函数里,完成了对方法名称的解析。
    在这里插入图片描述

  • 上图是PartTreeJpaQuery的构造函数,重点关注try里的第一行。
    在这里插入图片描述

  • 上图是PartTree类的属性,可以看到,利用正则表达式来匹配方法名。

  • 现在,我们已经知道:

  • 首先,在自定义Repository接口被创建时,由RepositoryFactorySupport创建代理,并在其构造函数中对此代理加入一个拦截器;

  • 之后,此拦截器,根据查询策略,对该接口的自定义方法进行解析,根据查询策略选取不同的RepositoryQuery实现进行处理,即得到可执行的持久化语句。

到这里可以推断出,第三种采用显示JPQL/SQL语句查询的方法也是类似的思路,因为这两种查询都可以在查询策略中设置

还有一些不明朗的问题,比如RepositoryFactorySupport是怎么创建代理的;怎么根据查询策略选取不同的RepositoryQuery实现的,具体处理过程是怎么样的,还有待后续学习…(立个flag 等我学会了就更新)

猜你喜欢

转载自blog.csdn.net/qq_36269372/article/details/83789443