部分内容参照 spring-data-jpa V2.6.5官方文档。
一、框架特性
- 基于Spring和JPA强大支持构建数据库
- 支持查询领域特定语言Querydsl(domain specification language)
- 模型类(实体类)的透明审计
- 分页查询、支持动态查询、整合自定义的数据访问程序
- 启动时注解查询验证
- 基于XML实体映射
- 注解@EnableJpaRepositories配置数据库
二、框架实战
框架核心的类就是Repository库。看一下该类的UML图。
框架支持两种查询方法,其一是根据方法名派生,又叫做命名查询,无需写SQL语言;其二是使用@Query注解,又称为注解查询。
2.1 命名查询
下表是命名查询支持的方法名及其对应的持久化查询语句。
命名查询支持的方法名
关键字 | 例子 | JPQL (Java持久化查询语言) |
|
|
|
|
|
… where x.lastname = ?1 and x.firstname = ?2 |
|
|
… where x.lastname = ?1 or x.firstname = ?2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IgnoreCase | findByFirstnameIgnoreCase |
… where UPPER(x.firstname) = UPPER(?1) |
2.2 注解查询@Query
2.2.1 基本用法
?是占位符,解释后面的数字代表的参数,称为基于位置的参数。
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress); }
2.2.2 like用法
%表示like表达式的占位符。
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.firstname like %?1") List<User> findByFirstnameEndsWith(String firstname); }
2.2.3 命名参数
默认使用基于位置的参数,也可以使用下述命名参数。这种方式写的太复杂,没有基于位置的好用。
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname") User findByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname); }
2.2.4 SpEL( Spring Expression Language)
@Query注解支持Spring表达式语言,这种方法也是太多复杂,不过我们遇到也要知道是可以这样写的。
@Entity public class User { @Id @GeneratedValue Long id; String lastname; } public interface UserRepository extends JpaRepository<User,Long> { @Query("select u from #{#entityName} u where u.lastname = ?1") List<User> findByLastname(String lastname); }
2.2.5 更新语句
JPA的更新操作很复杂,首先要开启事务,接着告诉框架是执行什么操作,最后是在@Query注解里写更新SQL。
@Transactional @Modifying @Query("update User u set u.firstname = ?1 where u.lastname = ?2") int setFixedFirstnameFor(String firstname, String lastname);
当然还有另一种方法就是,首先更新模型类,再执行save操作。这个过程执行了两次数据IO,第一次是查询旧的模型类,然后对象更新,再进行保存。
2.2.6 删除语句
interface UserRepository extends Repository<User, Long> { void deleteByRoleId(long roleId); @Transactional @Modifying @Query("delete from User u where u.role.id = ?1") void deleteInBulkByRoleId(long roleId); }
2.3 实体状态监测策略
版本和ID属性检测
2.4 排序
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.lastname like ?1%") List<User> findByAndSort(String lastname, Sort sort); @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%") List<Object[]> findByAsArrayAndSort(String lastname, Sort sort); } repo.findByAndSort("lannister", Sort.by("firstname")); repo.findByAndSort("stark", Sort.by("LENGTH(firstname)")); repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); repo.findByAsArrayAndSort("bolton", Sort.by("fn_len"));
2.5 表关系
2.5.1 如何设计库表
确定表与表之间的关联关系,一对一、一对多、多对多。
关系 | 使用频次 | 例子 | 备注 | 建表原则 |
一对一 | 较少 | 学生表 | 可以制作成一张表,特殊的一对多关系,太复杂时,采用分表策略 | 主表的主键和从表的外键(唯一),形成主外键关系,外键唯一unique;主表的主键和从表的主键,形成主外键关系 |
一对多 | 非常多 | 院系和学生,部门和员工,客户和订单,分类和商品 | 从表中建外键与主表进行关联 | 在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键 |
多对多 | 较多 | 学生表和课表,老师和学生表,用户和角色表 | 创建中间关系表 | 创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键 |
设计数据库表时,不要想太复杂,就根据这三种关系进行字段的设计。
2.5.2 JPA关系表操作步骤
使用注解标注列完成表关系的建立。
@OneToOne
@OneToMany
@ManyToMany
番外篇
FW1 Querydsl是啥
Querydsl是一个Java开源框架,它支持构造静态类型的类似sql的查询。不需要将查询写成内联字符串或外部化到XML文件中。目前 Querydsl 支持的平台包括 JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。
与简单的字符串相比,使用流畅的API的好处是:
- IDE中的代码完成
- 几乎不允许语法上无效的查询
- 可以安全地引用域类型和属性
- 更好地对域类型的更改进行重构