Spring Data JPA从入门到精通(第一部分)
传送门:
Spring Data JPA从入门到精通(第一部分)
Spring Data JPA从入门到精通(第二部分)
Spring Data JPA从入门到精通(第三部分)
所有代码均源自spring-data#2.2.6版本
第1章 整体认识JPA
- Spring Data的子项目:
- spring data common
- spring data gemfire
- spring data jpa
- spring data keyvalue
- spring data LDAP
- spring data MongoDB
- spring data Rest
- spring data Redis
- spring data for apache cassandra
- spring data for apache solr
可谓范围全面,其目标就是提供一个一致的,基于Spring的数据访问编程模型,同时仍能保留底层数据存储的特殊特性.包括了关系型DB,外部缓存Redis,流数据库cassandra,搜索引擎solr.本书重点还是关系型-JPA.
- Srping Data JPA 主要类结构图(图1-5) -- 目前已不是这样
image.png
第2章 JPA基础查询方法
- Spring Data Common 的Repository
@Indexed
public interface Repository<T, ID> {
}
SpringFramework5.0引入了一个注解@Indexed,在项目中使用了@Indexed之后,编译打包的时候会在项目中自动生成META-INT/spring.components文件,这样系统会自动给实体bean增加排序功能.
- Repository的类层次关系
- CrudRepository 接口
- PageAndSortingRepository 接口
- JpaRepository 接口
- Repository的实现类SimpleJpaRepository
- JpaRepositoryImplementation
public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
...
}
从定义可以看出,不但扩展了JpaRepository,而且还扩展了JpaSpecificationExecutor,里面都是查询的方法,而参数都是Specification<T>类型.
补充: 复习一下JDK的动态代理实现原理
第3章 定义查询方法
- 定义查询方法的配置方法
JPARepository实现原理是采用动态代理,因此只需要扩展Spring数据接口即可,或者用@RepositoryDefinition,书里却没有说用法.
- 方法查询策略设置
QueryLookupStray.Key
public static enum Key {
CREATE, USE_DECLARED_QUERY, CREATE_IF_NOT_FOUND;
}
默认为CREATE_IF_NOT_FOUND,等于前面2个结合.先查找是否有注解声明的SQL,如果没有,就根据方法名Create出来.
- 查询方法的创建
主要看PartTree源码,他根据前缀和属性名对方法进行切分,这个过程是可以嵌套的;例如
List<Person> findByAddressZipCode(String zipCode);
首先查找addressZipCode属性,发现没有,就根据驼峰命名找addressZip属性,还是没有,再找address属性,发现有了.然后后面的ZipCode继续找,发现正好有个同名.就part()出查询条件了.
(前提是Person有个Address属性,其中有个zipCode的String属性.)
- 查询结果的处理
- 查询结果的分页和排序
使用Pageable,Slice,Sort. - 限制查询结果
使用first,top
-
其他形式(Stream,Future) -- 略
-
Projections对查询结果的扩展
使用Projection可以对查询结果进行映射,用接口的方式,最简单是定义接口中属性为Entity的一个子集,这样自动就能映射到结果.同时也可以用@Value和SPEL表达式生成新的属性.甚至可以在Repository中使用泛型,由后续调用指定具体返回类型.
- 实现机制
QueryExecutorMethodInterceptor,居然只给了类图,这个从后面源码阅读来看,总结了JPA使用Spring FactroyBean怎么把接口编程代理对象的过程,是相当重要的.
图3-3
image.png
第4章 注解式查询方法
- @Query
public @interface Query {
// 指定的SQL查询语句
String value() default "";
// 指定的count语句,一般用default即可
String countQuery() default "";
// 指定那些字段来count,一般用default即可
String countProjection() default "";
// 是否为原生(直接放DB运行)的SQL,默认为JQL模式,非原生.
boolean nativeQuery() default false;
// 名字,默认的生成规则为
// {@code $ domainClass}.${queryMethodName}} will be used.
String name() default "";
// 同上,指定一个Count的查询名称,默认为name加上".count"
String countName() default "";
}
Query注解是直接写SQL语句查询的方式了,需要注意的是,如果使用Native=true(数据库绑定)的查询语句,Sort参数需要手工写入到语句中,而且对于分页支持不友好,尽量还是少用吧.
- @Param
Param给参数命名,这样再@Query中就可以不用"?1"这种顺序参数,而是使用参数名了,是的语句更为容易阅读.
- SpEl表达式的支持
可以直接用#{#entityname}的形式声明查询条件(${}为Properties表达式,而#entityname为保留字,JPA根据@Entity注解,读取里面的名称),这样from时候就知道表名了.
- @Modifying
这个用在Upate,Delete,Insert语句,声明语句执行后,参数数据发生了变化.
-
@QueryHints
-
@Procedure存储过程
- 先在实体Entity上使用@NamedStoredProcedureQueries注解声明存储过程的调用方式,其中参数需要用@StoredProcedureParamter声明,并且特别指定IN,OUT
- 然后在Repository上使用@Procdure注解调用该存储过程,也就是说存储过程内部用了什么表并不需要关心,只需要关心我们传入Repository的实体上有这个存储过程的声明.
- 注意@Param需要与IN匹配,返回类型必须与存储过程返回值类型匹配.
- 使用@Produce只能针对一个OUT的情况,如果存储过程有多个OUT,那就只能调用entityManager.createNamedStoredProcedureQuery,然后在调用store.getOutputParameterValue逐个获得返回值了.(这种情况,还是改改存储过程自身吧)
- @NamedQueries预定义查询 -- 不推荐
第5章 @Entity实例里面常用注解
- javax.persistence
- 注解列表.
- 关系注解
- @JoinColumn与@OneToOne,@OneToMany,@ManyToOne
- @OrderBy,用在@OneToMany多的那一方排序方法
- @@JoinTabe与@ManyToMany
- LeftJoin,InnerJoin与@EntityGraph
关联查询自动生成的语句经常需要多次查询,如先查询1的那张表,再生成查询n的那张表,通过@EntityGraph就能关联起来一次全部查询出来.
- 关系查询的坑
- 所有注解配置在字段或者全部配置在get方法,不能混用(这与@Autowired不同)
- 双向注解在Json序列化会产生死循环,需要手工转换,或者配合@JsonIgnore使用
- 表本身并不要求建立外键索引,使用@MappedBy需要注意
- 级联删除是危险操作,需要仔细评估
- @JoinColumn中name,referenceColumnName含义不易理解,可配合打印出的SQL调整
- 关联关系建议绑定到外键上,不用非外键的表间关联.