Spring Data JPA
ORM 持久层框架
一、解决的问题(原生 JDBC 存在)
- 数据库配置 - 硬编码 - 配置文档 (不常变动)
- SQL 语句、参数设置、获取结果集 - 硬编码 - 配置文档 (常变动)
- 手动封装结果集 - 繁琐 - 反射、内省
- 频繁创建、释放 数据库连接 - 资源浪费 - 连接池
- 简单 SQL 仍需编写 SQL 语句 - 动态代理
目的 : 简化数据库开发
二、名词解释
-
JPA - Java Persistence API - Java 持久化应用接口
仅仅是一套规范,由接口和抽象类组成,通常使用 Hibernate 作为该规范的实现
-
ORM - Object/Relation Mapping - 对象关系映射
通过操作 POJO 对象,达到操作数据库的目的
三、注解
- @Entity - 表示该类要交给 Spring Data JPA 进行管理
- @Table(name="表名") - 声明该 Entity 类对应的数据库表
- @Id - 声明该属性对应数据库主键
- @GeneratedValue(strategy = 生成策略) - 设置主键的生成策略
- GenerationType.IDENTITY - 依靠数据库的主键自增 - MySQL
- GenerationType.SEQUENCE - 依靠串行号生成主键 - Oracle
- @Column(name = "字段名") - 声明该属性对应数据表中的字段名
- @Query(value="SQL语句", nativeQuery=是否为原生SQL) - 声明该方法运行的SQL语句
- ?1、?2 - 形参占位符
- ?参数名 - 形参占位符
@Query("from Resume where id=?1 and name=?2")
public List<Resume> findByJpql(Long id,String name);
@Query(value = "select * from tb_resume where name like ?1 and address like ?2",nativeQuery = true)
public List<Resume> findBySql(String name,String address);
四、JPA 接口类
-
JpaRepository<实体类类型, 主键类型> - 定义基本内置接口方法
-
JpaSpecificationExecutor<实体类类型> - 定义动态查询接口方法
⼀个符合 SpringDataJpa 规范的 Dao 层接口是需要继承 JpaRepository 和 JpaSpecificationExecutor
public interface ResumeDao extends JpaRepository<Resume,Long>,JpaSpecificationExecutor<Resume> {
}
五、方法命名规则
findBy - 属性名(首字母大写) - 查找方式(模糊、等价) - 连接词(And、Or)
public List<Resume> findByNameLikeAndAddress(String name,String address);
查找方式
关键字 | 方法命名 | sql where字句 |
---|---|---|
Is,Equals | findById,findByIdEquals | where id= ? |
Between | findByIdBetween | where id between ? and ? |
LessThan | findByIdLessThan | where id < ? |
LessThanEqual | findByIdLessThanEqual | where id <= ? |
GreaterThan | findByIdGreaterThan | where id > ? |
GreaterThanEqual | findByIdGreaterThanEqual | where id > = ? |
After | findByIdAfter | where id > ? |
Before | findByIdBefore | where id < ? |
IsNull | findByNameIsNull | where name is null |
isNotNull,NotNull | findByNameNotNull | where name is not null |
Like | findByNameLike | where name like ? |
NotLike | findByNameNotLike | where name not like ? |
StartingWith | findByNameStartingWith | where name like '?%' |
EndingWith | findByNameEndingWith | where name like '%?' |
Containing | findByNameContaining | where name like '%?%' |
OrderBy | findByIdOrderByXDesc | where id=? order by x desc |
Not | findByNameNot | where name <> ? |
In | findByIdIn(Collection<?> c) | where id in (?) |
NotIn | findByIdNotIn(Collection<?> c) | where id not in (?) |
True | findByFlagTrue | where flag = true |
False | findByFlagFalse | where flag = false |
IgnoreCase | findByNameIgnoreCase | where UPPER(name)=UPPER(?) |
六、内置方法
-
根据 ID 查找 - Optional<实体类> optional = 接口类.findById(id)
Optional<Resume> optional = resumeDao.findById(1l); Resume resume = optional.get();
-
根据实体类对象属性精确查找 - Example<实体类> example = Example.of(实体类对象)
Optional<实体类> one = 接口类.findOne(example)
Resume resume = new Resume(); resume.setId(1l); resume.setName("张三"); Example<Resume> example = Example.of(resume); Optional<Resume> one = resumeDao.findOne(example); Resume resume1 = one.get();
-
添加或更新【透过实体类对象有无主键来区分】- 实体类 save = 接口类.save(实体类对象)
- 有主键 - 更新
- 无主键 - 添加
Resume resume = new Resume(); resume.setId(5l); resume.setName("赵六"); Resume save = resumeDao.save(resume);
-
根据 ID删除 - 接口类.deleteById(id)
resumeDao.deleteById(5l);
-
查找全部 - List<实体类> list = 接口类.findAll()
List<Resume> list = resumeDao.findAll();
-
排序 - Sort sort = new Sort(Sort.Direction.DESC, "字段名")
Sort sort = new Sort(Sort.Direction.DESC, "id"); List<Resume> list = resumeDao.findAll(sort)
-
分页 - Pageable pageable = PageRequest.of(0, 2)
如果想分页同时排序,排序的属性放在 PageRequest 的属性中
Pageable pageable = PageRequest.of(0,2); Page<Resume> all = resumeDao.findAll(pageable); Sort sort = new Sort(Direction.DESC, "id"); Pageable pageable = new PageRequest(page, size, sort); Page<Resume> all = resumeDao.findAll(pageable);
七、动态查找 Specification
// 根据条件查找单个对象
Optional<T> findOne(@Nullable Specification<T> var1);
// 根据条件查找所有
List<T> findAll(@Nullable Specification<T> var1);
// 根据条件查找并进行分页
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
// 根据条件查找并进行排序
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
// 根据条件统计
long count(@Nullable Specification<T> var1);
interface Specification<T> toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3); // 用来封装查找条件的
// Root:根属性(查找所需要的任何属性都可以从根对象中获取)
// CriteriaQuery 自定义查找方式 用不上
// CriteriaBuilder 查找构造器,封装了很多的查找条件(like = 等)
Specification<Resume> specification = new Specification<Resume>(){
@Override
public Predicate toPredicate(Root<Resume> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// 获取到name属性
Path<Object> name = root.get("name");
// 使用CriteriaBuilder针对name属性构建条件(精准查找)
Predicate predicate = criteriaBuilder.equal(name, "张三");
return predicate;
}
};
Specification<Resume> specification = new Specification<Resume>(){
@Override
public Predicate toPredicate(Root<Resume> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// 获取到name属性
Path<Object> name = root.get("name");
Path<Object> address = root.get("address");
// 条件1:使用CriteriaBuilder针对name属性构建条件(精准查找)
Predicate predicate1 = criteriaBuilder.equal(name, "张三");
// 条件2: address 以"北"开头(模糊匹配)
// 模糊查找的时候需要转换成 Expression 类型
Predicate predicate2 = criteriaBuilder.like(address.as(String.class), "北%");
// 组合两个条件
Predicate and = criteriaBuilder.and(predicate1, predicate2);
return and;
}
};
Optional<Resume> optional = resumeDao.findOne(specification);
Resume resume = optional.get();