【Java劝退师】Spring Data JPA 知识脑图 - ORM持久层框架

813 阅读4分钟

Spring Data JPA

Spring Data JPA

ORM 持久层框架

一、解决的问题(原生 JDBC 存在)

  1. 数据库配置 - 硬编码 - 配置文档 (不常变动)
  2. SQL 语句、参数设置、获取结果集 - 硬编码 - 配置文档 (常变动)
  3. 手动封装结果集 - 繁琐 - 反射、内省
  4. 频繁创建、释放 数据库连接 - 资源浪费 - 连接池
  5. 简单 SQL 仍需编写 SQL 语句 - 动态代理

目的 : 简化数据库开发

二、名词解释

  1. JPA - Java Persistence API - Java 持久化应用接口

    仅仅是一套规范,由接口和抽象类组成,通常使用 Hibernate 作为该规范的实现

  2. ORM - Object/Relation Mapping - 对象关系映射

    通过操作 POJO 对象,达到操作数据库的目的

三、注解

  1. @Entity - 表示该类要交给 Spring Data JPA 进行管理
  2. @Table(name="表名") - 声明该 Entity 类对应的数据库表
  3. @Id - 声明该属性对应数据库主键
  4. @GeneratedValue(strategy = 生成策略) - 设置主键的生成策略
    • GenerationType.IDENTITY - 依靠数据库的主键自增 - MySQL
    • GenerationType.SEQUENCE - 依靠串行号生成主键 - Oracle
  5. @Column(name = "字段名") - 声明该属性对应数据表中的字段名
  6. @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 接口类

  1. JpaRepository<实体类类型, 主键类型> - 定义基本内置接口方法

  2. 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,EqualsfindById,findByIdEqualswhere id= ?
BetweenfindByIdBetweenwhere id between ? and ?
LessThanfindByIdLessThanwhere id < ?
LessThanEqualfindByIdLessThanEqualwhere id <= ?
GreaterThanfindByIdGreaterThanwhere id > ?
GreaterThanEqualfindByIdGreaterThanEqualwhere id > = ?
AfterfindByIdAfterwhere id > ?
BeforefindByIdBeforewhere id < ?
IsNullfindByNameIsNullwhere name is null
isNotNull,NotNullfindByNameNotNullwhere name is not null
LikefindByNameLikewhere name like ?
NotLikefindByNameNotLikewhere name not like ?
StartingWithfindByNameStartingWithwhere name like '?%'
EndingWithfindByNameEndingWithwhere name like '%?'
ContainingfindByNameContainingwhere name like '%?%'
OrderByfindByIdOrderByXDescwhere id=? order by x desc
NotfindByNameNotwhere name <> ?
InfindByIdIn(Collection<?> c)where id in (?)
NotInfindByIdNotIn(Collection<?> c)where id not in (?)
TruefindByFlagTruewhere flag = true
FalsefindByFlagFalsewhere flag = false
IgnoreCasefindByNameIgnoreCasewhere UPPER(name)=UPPER(?)

六、内置方法

  1. 根据 ID 查找 - Optional<实体类> optional = 接口类.findById(id)

    Optional<Resume> optional = resumeDao.findById(1l);
    Resume resume = optional.get();
    
  2. 根据实体类对象属性精确查找 - 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();
    
  3. 添加或更新【透过实体类对象有无主键来区分】- 实体类 save = 接口类.save(实体类对象)

    • 有主键 - 更新
    • 无主键 - 添加
    Resume resume = new Resume();
    resume.setId(5l);
    resume.setName("赵六");
    Resume save = resumeDao.save(resume);
    
  4. 根据 ID删除 - 接口类.deleteById(id)

    resumeDao.deleteById(5l);
    
  5. 查找全部 - List<实体类> list = 接口类.findAll()

    List<Resume> list = resumeDao.findAll();
    
  6. 排序 - Sort sort = new Sort(Sort.Direction.DESC, "字段名")

    Sort sort = new Sort(Sort.Direction.DESC, "id");
    List<Resume> list = resumeDao.findAll(sort)
    
  7. 分页 - 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();