以下内容纯属个人扯淡,仅供参考,建议拜读原著
目录
附录1:Repository Query Method 关键字列表
附录2:Repository Query Method 返回值类型
附录5:application.properties中关于JPA的配置大全
读后感
2020/8/18
个人感觉这本书很不好。
第1:书名是:SpringDataJpa从入门到精通,作为工具书关于SpringDataJpa的广度又不够,深度方面更加是浅的离谱,其他的实战类的书是点到为止,但这里是点都不全。有关广度这个问题,书里又花了将近一半的文字在讲其他内容:SpringData、SpringDataRedis、SpEL表达式等,甚至IDEA部分的图片和描述占据了很大篇幅。
第2:之前看Spring实战,各个章节分的很清楚,而且基本整体都是总-分结构,先整体结构再局部描述。而这本书说实话章节安排有些乱
第3:博客有错别字我能理解,书上有些地方有错别字,而且有些地方行文逻辑都是难以看懂。例如下面这句话,逗号前一句和逗号后一句话,这两句话逻辑是这样:可以通过......。要创建.....
第4:书的文字部分,有些感觉是东拼西凑来的,所以总是读的一段一段,思路跳跃的厉害
总之一句话:看的很难受。所以,本人只是看了2-7章,个人观点,不喜勿喷。
倒不如参考一篇博客:Spring data jpa 复杂动态查询方式总结、QueryDSL:使用QueryDSL
这篇博客开篇就说:不太推荐使用jpa做ORM框架,因为对于复杂查询时是不太灵活的,而是建议使用mybatis框架自己写sql,jpa对于简单查询是十分方便的。
1、核心方法
查询所有数据 findAll()
修改 添加数据 S save(S entity)
分页查询 Page<S> findAll(Example<S> example, Pageable pageable)
根据id查询 findOne()
根据实体类属性查询: findByProperty (type Property); 例如:findByAge(int age)
删除 void delete(T entity)
计数 查询 long count() 或者 根据某个属性的值查询总数 countByAge(int age)
是否存在 boolean existsById(ID primaryKey)
2、查询关键字(方法命名方式)
3、注解
@Modifying、@Query、@Transaction、@Async
4、继承JpaSpecificationExecutor接口进行复杂查询
可以实现多条件分页
5、引入QueryDSL
QueryDSL是基于各种ORM框架以及SQL之上的一个通用API的查询框架
第一部分:基础
第1章:整体认识JPA
概览
ORM框架对比
JPA规范
SpringData项目
SpringDataJPA
MySQL快速开发实例
1、ORM框架对比
1、MyBatis
侧重于POJO与SQL之间的映射关系,可以进行更为细致的SQL,目前占有率最高,上手容易,适合互联网应用公司开发API场景
2、Hibernate
侧重于对象与对象之间的关系
对JDBC进行了非常轻量级的对象封装。有自己的HQL查询语言,数据库移植性很好
符合JPA规范,上手难,适合企业级应用系统开发
3、Spring Data JPA
JPA规范的再次封装,底层使用Hibernate+JPA实现
引用JPQL查询语言,属于Spring生态一部分
上手简单
2、JPA规范
全称Java Persistence API(Java持久层API),是JDK5.0注解/xml描述对象关系表的映射关系(关系型数据库),并将运行期实体对象持久化到数据库中,Sun引入JPA规范的原因:简化现有JavaEE和JavaSE应用开发;整合ORM技术,实现大一统(mybatis不符合JPA规范)。其内容包括3个部分:
1、一套API标准
在javax.persistence包下
用于操作实体对象,执行CRUD操作
2、面向对象查询语言(JPQL)
避免程序的SQL紧密耦合
3、ORM元数据的映射
JPA支持xml、注解两种元数据的方式(元数据描述着对象和表之间的映射关系)
3、SpringData项目
致力于提供一个一致的、基于Spring的数据访问编程模型,支持关系数据库、NoSQL、基于云的数据服务。SpringDataCommon是所有模块的公有部分,它提供了技术中立的库接口、一个坚持Java类的元数据模型,它的子项目有:
主要子项目:Commons、Gemfire、JPA、KeyValue、LDAP、MongoDB、REST、Redis、Apache Cassandra、Apache Solr
社区支持的子项目:Aerospike、Couchbase、DynamoDB、Elasticsearch、Hazelcast、Jest、Neo4j、Vault、JDBC Extensions、Hadoop、Spring Content
4、SpringDataJPA
主要类
7个Repository接口
Repository
CrudRepository
PagingAndSortingRepository
QueryByExampleExecutor
JpaRepository
JpaSpecificationExecutor
QueryDslPredicateExecutor
2个实现类
SimpleJpaRepository
QueryDslJpaRepository
JPA底层封装类
EntityManager(javax.persistence)
EntityManagerImpl(org.hibernate.jpa.internal)
类结构
5、MySQL快速开发实例
(1)环境要求:JDK1.8+、Maven3.0+、IDEA
(2)数据库及表
数据库名=db_example,表名=user
CREATE TABLE `user` {
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`email` varchar(200) DEFAULT NULL,
PRIMARY KEY ('id')
}
(3)创建项目
IDEA中的Spring Initializr创建,选择Web、JPA、MySQL模块
(4)application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/db_example
spring.datasource.username=xxxx
spring.datasource.password=xxxx
(5)创建User实体
@Entity
public class User {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
private String email;
getter/setter
}
(6)UserRepository
public class UserRepository extends CrudRepository<User,Long> {
}
(7)UserController
@Controller
@RequestMapping(path = "/demo")
public class UserController{
@Autowired
private UserRepository userRepository;
@GetMapping("/add")
public void addUser( @RequestParam String name, @RequestParam String email ){
User n = new User();
n.setName(name);
n.setEmail (email);
userRepository.save(n);
}
@GetMapping("/all")
public Iterable<User> getAllUsers(){
return userRepository.findAll();
}
}
第2章:JPA基础查询方法
概览
Repository概述
分析方法
核心类分析
CrudRepository
总结:主要是记住Repository等这些接口之间的关系和和api概览;实际上使用时是使用SimpleJpaRepository。但有个疑问:如果我们程序中新建接口直接继承CrudRepository,框架通过动态代理生成对象,但貌似生成对象实例却是SimpleJpaRepository呢?
疑问:NoRepositoryBean、QueryByExampleExecutor两个类的分析呢?
1、Repository概述
该接口位于Spring Data Common/lib中,是Spring Data做数据库操作最底层的抽象接口、最顶级的父类。该接口为空接口,仅仅作为一个标识作用(用于捕获要使用的类型,用户也可以扩展此接口,Spring底层做动态代理时就会发现:只要是它的子类,就都代表存储库操作)。该泛型接口的类型参数:T是领域类,ID是该领域类的id
public interface Repository<T, ID extends Serializable> {
}
2、分析方法
1》查看类的继承结构
打开Repository.class类,Navigate/Type Hierarchy(快捷键:Ctrl + H)。注意:要找到最顶层的父级接口,向下级分析
Repository
RevisionRepository
ReactiveCrudRepository
ReactiveSortingRepository
RxJava2CrudRepository
RxJava2SortingRepository
CrudRepository
PagingAndSortingRepository
KeyValueRepository
SimpleKeyValueRepository
QuerydslKeyValueRepository(+QuerydslPredicateExecutor)
JpaRepository(+QueryByExampleExecutor)
JpaRepositoryImplementation(JpaSpecificationExecutor)
SimpleJpaRepository
QuerydslJpaRepository(+QuerydslPredicateExecutor)
2》查看类的UML关系
打开某个类(一般是较低级的具体实现类),右键/Show Diagrams(快捷键:Ctrl+Shift+Alt+U)。me:打开QueryDslJpaRepository,如下:
7个Repository接口,2个实现类(SimpleJpaRepository、QueryDslJpaRepository)
注意:选中某个类F4,可以查看该类源码
3》查看类成员
快捷键:Ctrl+F12是简单弹框版,Alt+7是打开File Structure。功能是一样的(勾选左上角选项,可以看到继承下来的成员)
3、核心类分析
1》CrudRepository
package org.springframework.data.repository;
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
//有2句sql:第1句查询该实体是否存在;第2句是新增/更新
//判断是否存在有两种机制:根据主键判断;根据Version判断
<S extends T> S save(S entity);
//批量保存。for循环上一个方法
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
//根据主键查询
Optional<T> findById(ID id);
//根据主键判断是否存在
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
//这里也会先查询是否存在
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
该接口提供公共的基本CRUD方法。默认提供一个具体实现类SimpleJpaRepository
问题:Java多态。我们如果用一个接口,如:AbcRepository接口去直接继承CrudRepository接口,那么AbcRepository.save()实际使用的是SimpleJpaRepository?报错:ITestB it = new TestA(),因为类TestA并不是ITestB接口的子类
public interface ITest {
void test();
}
public class TestA implements ITest {
@Override
public void test() {
System.out.println("撒老大看见");
}
}
public interface ITestB extends ITest {
}
2》PagingAndSortingRepository
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
总结:该接口再CrudRepository的基础上增加了排序、分页功能
3》JpaRepository
上述1》、2》类是在spring-data-commons包中,是为了兼容NoSQL而进行了一些抽象封装。从该接口这层往下是对关系型数据库进行抽象封装,它继承了PagingAndSortingRepository,实现类仍然是SimpleJpaRepository,除此之外还继承了QueryByExampleExecutor接口。它在1》、2》的基础上多了QueryByExample条件查询、批量查询的返回类型不再是Iterator而是List。
package org.springframework.data.jpa.repository;
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
4》SimpleJpaRepository
该类是JPA整个数据库相关Repository的接口实现类,可以继承该类进行扩展(如:JPA提供的QueryDsl)。该类也是Spring动态代理的实现类。通过EntityManager进行实体操作,JpaEntityInForMation保存了实体的相关信息及crud方法元数据
第3章:定义查询方法(方法命名方式)
总结:本章主要是利用方法名来定义查询方法,SpringDataJpa将根据方法名构造出查询。由于Spring JPA Repository是采用动态代理机制,因此这里有2种定义查询方法:
1、从方法名称中可以指定特定用于存储的查询和更新(第3章/本章);
本人后面统一称之为:方法命名方式
2、通过使用@Query手动定义(第4章)
本人后面统一称之为:注解声明方式
1、定义查询方法的配置方法
2、设置查询策略
在SpringBoot启动类上来设置:
@EnableJpaRepositoies(queryLookupStrategy=QueryLookupStrategy.CREATE_IF_NOT_FOUND)
(SpringBoot启动类)
CREATE:
直接根据方法名进行创建(即:只限于方法命名方式)
规则:根据方法名称的构造进行尝试
从方法名中删除给定的一组已知前缀,并解析该方法的其余部分
若方法名不符合规则,则应用启动会报错
USE_DECLARED_QUERY:
声明式创建(第4章)(即:只限于注解声明方式)
启动时会尝试找到一个声明的查询,若没找到则抛异常
查询可以由某处注释或其他方法声明
CREATE_IF_NOT_FOUND:
默认值
上述两方法结合:先用声明式查找,若失败则尝试根据方法名创建
结论:默认就是2种方式结合。优先级是先注解再方法名,下面是一些具体策略类的关系:
3、方法命名方式
框架中有个根据方法名的查询生成器机制。一般用于:对于实体类型上构建约束查询(对实体单表的约束查询),其规则如下
1、查询策略关键字
2、查询字段
3、限制性条件
1》基本使用
interface PersonRepository extends Repository<User, Long> {
//where emailAddress=? and lastname=?
List<User> findByEmailAddressAndLastname(EmailAddress emailAddress,String lastname);
//where lastname=? or firstname=?,并去重
List<User> findDistinctPeopleByLastnameOrFirstname(String lastname,String firstname);
List<User> findPeopleDistinctByLastnameOrFirstname(String lastname,String firstname);
//where lastname(忽略大小写)=?
List<User> findByLastnameIgnoreCase(String lastname);
//where lastname(忽略大小写)=? and firstname(忽略大小写)=?
List<User> findByLastnameAndFirstnameAllIgnoreCase(String lastname,String firstname);
//where lastname=? ,并根据firstname排序(Asc、Desc)
List<User> findByLastnameOrderByFirstnameAsc(String lastname,String firstname);
}
解析方法的实际结果取决于创建查询方法的持久性存储。注意事项
1、表达式通常是可以连接的运算符的属性遍历
可以使用组合属性表达式:AND、OR
也可以将运算关键字Between、LessThan、GreaterThan、Like作为属性表达式
受支持的操作元可能因数据存储而已(参考官方文档)
2、该方法解析器支持设置一个IgnoreCase标识个别特性,例如:
findByLastnameIgnoreCase、findByLastnameAndFirstnameAllIgnoreCase支持忽略大小写
受支持的操作元可能因数据存储而已(参考官方文档)
3、可通过OrderBy在引用属性和提供排序方向的查询方法中,附加一个子句来应用静态排序
2》关键字列表
关键字 | 方式1:方法命名 | 方式2:注解声明。@Query(见第4章) 对应的JPQL表达示例 x表示映射到表的类对象 |
And Or | findByLastnameAndFirstname findByLastnameOrFirstname | where x.lastname = ?1 and x.firstname = ?2 where x.lastname = ?1 or x.firstname = ?2 |
Is、Equals Not | findByFirstname findByFirstnameIs findByFirstnameEquals findByFirstnameNot | where x.firstname = ?1 where x.firstname <> ?1 |
LessThan LessThanEqual GreaterThan GreaterThanEqual | findByAgeLessThanEqual | where x.age <= ?1 |
Between | findByStartDateBetween | where x.startDate between ?1 and ?2 |
After Before | findByStartDateAfter | where x.startDate > ?1 |
IsNull IsNotNull、NotNull | findByAgeIsNull | where x.age is null |
Like NotLike StartingWith EndingWith Containing | findByFirstnameLike 均添加在末尾 | where x.firstname like ?1 not like ?1 like %?1 like ?1% like %?1% |
OrderBy | findByAgeOrderByLastnameDesc | where x.age = ?1 order by lastname desc |
In NotIn | findByAgeIn(Collection<Age> ages) | where x.age in ?1 |
True False | fingByActiveTrue | where x.active = true |
IgnoreCase | findByFirstnameIgnoreCase | where UPPER(x.firstname) = UPPER(?1) |
注意:除了find关键字外,查看PartTree还有以下关键字,并有相应的返回类型
1、Query_PATTERN:find、read、get、query、stream
返回类型为List<T>
2、COUNT_PATTERN:count
long
3、EXISTS_PATTERN:exists
boolean
4、DELETE_PATTERN:delete、remove
List<T>
3》方法查询策略中的属性表达式
4、查询结果的处理
5、实现机制介绍
TODO:看一下相关的源码
第4章:注解式查询方法(注解声明方式)
概览
@Query
@Param
SpEL表达式的支持
@Modifying(修改查询)
@QueryHints(Query优化)
@Procedure(存储过程的查询方法)
@NamedQueries(预定义查询)
1、@Query
源码
public @interface Query {
//指定JPQL的查询语句。(nativeQuery=true 的时候,是原生的SQL语句)
String value() default "";
//指定count的JPQL语句,如果不指定将根据query自动生成。(nativeQuery=true,原生的SQL语句)
String countQuery() default "";
//根据哪个字段来count,一般默认即可。
String countProjection() default "";
//默认是false,表示value里面是不是原生的Sql语句
boolean nativeQuery() default false;
//可以指定一个query的名字,必须是唯一的。如果不指定,默认的生成规则是:
//{SdomainClass}.${queryMethodName}
String name() default "";
//可以指定一个count的query 名字,必须是唯一的。如果不指定,默认的生成规则是:
//{SdomainClass}.${queryMethodName}.count
String countName() default "";
}
1》简单CRUD
public interface UserRepository extends JpaRepository<User, Long> {
}
1)条件查询
@Query("select u from User u where u.email = ?1")
User findByEmail(String email);
注意:1)、2)中是JPQL语言,因此是类名User、属性名email,而不是数据表名、字段名
2)like模糊
@Query("select u from User u where u.name like%?1")
List<User> findByFirstnameEndsWith(String name);
注意:需要手动加上%,模糊查询的符号
3)原始SQL
@Query(value="select * from user where email = ?1",nativeQuery=true)
User findByEmail(String email);
4)原始SQL+排序
@Query(value="select * from user_info where first_name=?1 order by ?2",nativeQuery=true)
List<UserInfoEntity> findByFirstName(String firstNanme,String sort);
注意:nativeQuery是不支持Sort的参数查询,因此上述方法第2个参数是String类型而不是Sort类型,并且使用时传入的是数据库表的字段名而不是对象的属性名,如,根据last_name字段排序:
repository.findByFirstName("jack","last_name");
2》排序
@Query在JPQL下实现排序,直接使用PageRequest、Sort参数即可
1、
@Query(value="select u from User u where u.lastname like ?1%")
List<UserInfoEntity> findByAndSort(String lastnanme,Sort sort);
调用示例:
repo.findByAndSort("lannister",new Sort("firstname"));
repo.findByAndSort("stark",new Sort("LENGTH(firstname)"));
repo.findByAndSort("targaryen",JpaSort.unsafe("LENGTH(firstname)"));
2、
@Query(value="select u。id,LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
List<Object[]> findByAsArrayAndSort(String lastnanme,Sort sort);
调用示例:
repo.findByAsArrayAndSort("bolton",new Sort("fn_len"));
疑问:这里代码没看懂,是想完成什么业务?
3》分页
1、
@Query(value="select u from User u where u.lastname like ?1%")
List<UserInfoEntity> findByAndSort(String lastnanme,Sort sort);
调用示例:
repo.findByAndSort("lannister",new Sort("firstname"));
repo.findByAndSort("stark",new Sort("LENGTH(firstname)"));
repo.findByAndSort("targaryen",JpaSort.unsafe("LENGTH(firstname)"));
2、
@Query(value="select u。id,LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
List<Object[]> findByAsArrayAndSort(String lastnanme,Sort sort);
调用示例:
repo.findByAsArrayAndSort("bolton",new Sort("fn_len"));
2、@Param
3、SpEL表达式的支持
4、@Modifying(修改查询)
5、@QueryHints(Query优化)
6、@Procedure(存储过程的查询方法)
7、@NamedQueries(预定义查询)
第5章:@Entity实例中常用注解详解
概览
javax.persistence
基本注解
@Entity
@Table
@Id
@IdClass
@GeneratedValue
@Basic
@Transient
@Column
@Temporal
@Enumerated
@Lob
关联关系注解
@JoinColumn
@OneToOne
@OneToMany、@ManyToOne
@OrderBy
@JoinTable
@ManyToMany
其他:
left join、inner join
@EntityGraph
1、javax.persistence
2、基本注解
3、关联关系注解
4、其他
第二部分:高级
第6章:JpaRepository扩展详解
1、QueryByExampleExecutor
2、JpaSpecificationExecutor
3、自定义Repository
第7章:Spring Data JPA的扩展
第8章:DataSource的配置
1、默认DataSource
2、Ali Druid DataSource
3、事务
4、配置多数据源
5、Naming命名策略