初始化过程:
springboot自动找到继承了repository的类,为他们进行实例化。
spring data jpa 提供了
SimpleJpaRespositry :实现所有默认的Repository接口,中默认的操作,如findAll,delete等
JpaRepositoryFactoryBean:FactoryBean,顾名思义,就是将repository中的bean进行装配,然后以继承repository类的类名作为key,放入 DeafultListableBeanFactory中,在我们需要的时候,从这个bean中去对应的代理进行注入。
JdkDynamicAopProxy:动态代理的处理类,每一个方法都会经过这个拦截,然后进行处理
附上一张时序图: 图片来源:(http://www.luckyzz.com/java/spring-data-jpa/ )
DEBUG流程解决:
重点在于 RepositoryFactorySupport 中对我们的repository方法进行了拦截处理,执行excute方法,execute方法有两个实现类,KeyValuePartTreeQuery(用于常用的是实体query,如findAllByName)与AbstractJpaQuery(用于带有@Query注解),这边我们重点了解AbstractJpaQuery
-
- AbstractJpaQuery
AbstractJpaQuery 调用 了JpaQueryExecution来实现execute方法,execution中 有多个语句执行器,看对应的返回值,选择不同的执行器,常用的就是collectionExecution,和pageExecution.
从上面的图中,我们可以看到,jpa其实也是拦截器和反射的方法,对query中进行重新的赋值,然后进行语句的执行。
spring data jpa 提供了多个query的实现 (来源于 https://www.jianshu.com/p/fafd058911ca?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation )
- SimpleJpaQuery 适用方法:使用@Query注解,但nativeQuery为隐式或显示声明为false,即使用JPQL语法解析。
- NativeJpaQuery 适用方法:使用@Query注解,且nativeQuery显示声明为true,即使用原生sql语法解析。
- NamedQuery 适用方法:在entity上声明@NamedQuery注解,并在repository中使用该方法。
- PartTreeJpaQuery 适用方法:未使用@Query注解的方法,即根据方法名反射entity字段获取sql。
- StoredProcedureJpaQuery 适用方法:使用@Procedure注解的存储过程方法
下面看到的是NativeJpaQuery中createQuery的实现,可以看到就是先进行了排序语句的创建,然后在进行Jpa语句的创建。
重点来了,为什么我们在平常的编写中,有时候List<map>能够接受返回值,有时候必须要List<object[]>才能够接收呢,原因在于上面红色标注的地方,JPA在装配Bean的时候,会调用构造方法,在构造方法中,会对@query上的注解进行预处理,然后进行返回类型变量的存储,(存储在returnType中),在上面的红色圈内,进行了执行语句的判断,如下图所示。
上面几个图,可以看到的就是我们进行了一堆正则的判断,那到底是干嘛用的,其实就只是判断这个执行的语句是否有别名和默认的映射,如果存在别名和默认的映射,且相同,那么我们设置的returnType就直接返回result了,没有返回Tuple.class的类型,导致在装配返回值的时候,出现了nameIndexOut的报错,因为我们的字段名称name 为empty。
综上所述:
我们根据正则表达式,我们要让上面的getProjection()与alias不相等,所以正则需要匹配到,从正则表达式可以看到,getProjection()用的正则匹配没有支持大写,所以我们一般的@query中的sql语句,select 与 from 最好写成小写,然后最好出现"\n"的换行符。
来源:oschina
链接:https://my.oschina.net/u/4189935/blog/3226556