SpringDataJPA 系列之快速入门

时间:2022-07-26
本文章向大家介绍SpringDataJPA 系列之快速入门,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1.1 简介

1.1.1 概述

  Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!Spring Data JPA 让我们解脱了 DAO 层的操作,基本上所有 CRUD 都可以依赖于它来实现,在实际的工作工程中,推荐使用 Spring Data JPA + ORM(如:hibernate) 完成操作,这样在切换不同的 ORM 框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦

1.1.2 SpringDataJPA 与 JPA 和 hibernate 之间的关系

  JPA 是一套规范,内部是有接口和抽象类组成的。hibernate 是一套成熟的 ORM 框架,而且 Hibernate 实现了 JPA 规范,所以也可以称 hibernate 为 JPA 的一种实现方式,我们使用 JPA 的 API 编程,意味着站在更高的角度上看待问题(面向接口编程)。SpringDataJPA是 Spring 提供的一套对 JPA 操作更加高级的封装,是在 JPA 规范下的专门用来进行数据持久化的解决方案。

1.1.3 整合 SpringDataJPA

☞ SpringBoot 整合 SpringDataJPA ☞ Spring 整合 SpringDataJPA

1.2 SpringDataJPA 的使用

1.2.1 使用接中口定义的方法

  SpringDataJPA 致力于减少数据访问层的开发量,开发者唯一要做的就是声明持久层的接口,其他都交给SpringDataJPA来帮你完成。一般我们会继承 JpaRepository 和 JpaSpecificationExecutor 接口,我们可以使用接口中定义的方法进行查询。   ♞ CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法   ♞ PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法   ♞ JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法,我们一般继承这个。   ♞ JpaSpecificationExecutor: 不属于 Repository 体系,实现一组 JPA Criteria 查询相关的方法

☞ JpaRepository 中定义的方法

☞ JpaSpecificationExecutor 中定义的方法

☞ 接口方法速查

1.2.2 使用 JPQL

  使用 SpringDataJPA 提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用 @Query 注解,结合 JPQL 的语句方式完成查询。@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个 JPQL 查询语句即可。☞ JPQL 详细介绍

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/7/30
 * @description student repository 接口
 */
public interface StudentRepository extends JpaRepository<Student, Long> {
    @Query("from Student")
    public List<Student> findStudent();
}

  此外,也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询。可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作,注意:JPQL 不支持使用 INSERT 操作。方法的返回值是 int,表示更新语句所影响的行数。默认情况下,SpringDataJPA 的每个方法上有事务, 但都是一个只读事务,他们不能完成修改操作,因此需要在调用的地方必须加事务(添加 @Transactional 注解),没有事务不能正常执行。

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/7/30
 * @description student repository 接口
 */
public interface StudentRepository extends JpaRepository<Student, Long> {
	//**************************************************************************************
	// 第一种写法
	//**************************************************************************************
    @Modifying
    // ? 后的数字表示第几个参数,顺序与参数一致可不写
    @Query("update Student set sex = ?1, Name = ?2 where id = ?3") 
    public int updateStudent(Boolean sex, String Name, Long id);

	//**************************************************************************************
	// 第二种写法
	//**************************************************************************************
	@Modifying
    @Query("update Student set sex = :sex, Name = :Name where id = :id")
    public int updateStudent(@Param("sex") Boolean sex, @Param("Name") String Name, @Param("id") Long id);
}

1.2.3 使用 SQL 语句

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/7/30
 * @description student repository 接口
 */
public interface StudentRepository extends JpaRepository<Student, Long> {
	// nativeQuery true: 使用 sql 查询; false: 使用 jpql 查询,默认就是 false
	// 占位符与参数位置对应可不写 ? 后的数字
    @Query(value = "select * from student where id = ?1 or sex = ?2", nativeQuery=true)
    public List<Student> updateStudent(Long id, Boolean sex);
}

1.2.4 方法命名规则查询

  顾名思义,方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照 SpringDataJPA 提供的方法命名规则定义方法的名称,就可以完成查询工作。SpringDataJPA 在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询。按照 SpringDataJPA 定义的规则,查询方法以 findBy 开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。

☞ 支持的查询关键字

关键字

示例

JPQL语句

AND

findByLastnameAndFirstname

where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

where x.lastname = ?1 or x.firstname = ?2

Between

findByStartDateBetween

where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

where x.age < ?1

GreaterThan

findByAgeGreaterThan

where x.age > ?1

After

findByStartDateAfter

where x.startDate > ?1

Before

findByStartDateBefore

where x.startDate < ?1

IsNull

findByAgeIsNull

where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

where x.age not null

Like

findByFirstnameLike

where x.firstname like ?1

NotLike

findByFirstnameNotLike

where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

where x.firstname like ?1(parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

where x.firstname like ?1(parameter bound with prepended %)

Containing

findByFirstnameContaining

where x.firstname like ?1(parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

where x.lastname <> ?1

In

findByAgeIn(Collection ages)

where x.age in ?1

NotIn

findByAgeNotIn(Collection age)

where x.age not in ?1

True

findByActiveTrue()

where x.active = true

False

findByActiveFalse()

where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

where UPPER(x.firstame) = UPPER(?1)

☞ 示例

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/7/30
 * @description student repository 接口
 */
public interface StudentRepository extends JpaRepository<Student, Long> {
    public List<Student> findByAge(int age);
}

☞ 查询方法流程解析

  假如创建如下的查询:findByClassUserAge(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为 Student,先判断 ClassUserAge(根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性则从右往左截取第一个大写字母开头的字符串(此处为Age),然后检查剩下的字符串(classUser)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则继续从右往左截取;假设 class 为查询实体的一个属性,则先判断 class 是否有 userAge 属性,有则按照 Student.class.userAge 查询,没有就会报错。   可能会存在一种特殊情况,比如 Student 包含一个 class 的属性,也有一个 classUser 属性,此时会存在混淆。可以明确在属性之间加上 _ 以显式表达意图,比如 findByClass_UserAge() 或者 findByClassUser_Age。还有使用一些特殊的参数来完成某些操作,比如说分页操作:Page<UserModel> findByName(String name, Pageable pageable);排序操作:List<UserModel> findByName(String name, Sort sort)

1.2.5 Specifications 动态查询

  有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在 SpringDataJPA 中可以通过 JpaSpecificationExecutor 接口查询。相比 JPQL 其优势是类型安全,更加的面向对象。 JpaSpecificationExecutor 这个接口基本是围绕着 Specification 接口来定义的。我们可以简单的理解为,Specification 构造的就是查询条件。Specification 接口中只定义了如下一个方法:public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); 参数说明:  ♞ root:Root 接口,代表查询的根对象,可以通过 root 获取实体中的属性;  ♞ query:代表一个顶层查询对象,用来自定义查询;  ♞ cb:用来构建查询,此对象里有很多条件方法。 我们写的 Repository 接口需要继承 JpaSpecificationExecutor 之后才能进行动态查询,如下所示:

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/7/30
 * @description student repository 接口
 */
public interface StudentRepository extends JpaRepository<Student, Long>, JpaSpecificationExecutor<Student> {}

☞ 基于 Specifications 完成条件查询

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/7/30
 * @description 测试类
 */
@RunWith(SpringRunner.class)
@SpringBootTest()
public class Demo {
    @Autowired
    private StudentRepository studentRepository;

    @Test
    public void test() {
        // 使用匿名内部类的方式,创建一个 Specification 的实现类,并实现 toPredicate 方法
        Specification<Student> spec = new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                // cb: 构建查询,添加查询方式   like:模糊匹配
                // root:从实体 Student 对象中按照 Name 属性进行查询
                return cb.like(root.get("Name").as(String.class), "张");
            }
        };
        List<Student> one = studentRepository.findAll(spec);
        System.out.println(one);
    }
}

☞ 基于 Specifications 的分页查询

  对于 SpringDataJPA 中的分页查询,是其内部自动实现的封装过程,返回的是一个 SpringDataJPA 提供的 pageBean 对象。其中的方法有 int getTotalPages():获取总页数;long getTotalElements():获取总记录数;List<T> getContent():获取列表数据

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/7/30
 * @description 测试类
 */
@RunWith(SpringRunner.class)
@SpringBootTest()
public class Demo {
    @Autowired
    private StudentRepository studentRepository;

    @Test
    public void test() {

        // PageRequest 实现了 Pageable接口,调用静态方法 of 第一个参数:页码(从0开始); 第二个参数:每页查询条数
        Pageable pageable = PageRequest.of(0, 1);

        // 分页查询
        Page<Student> page = studentRepository.findAll(pageable);

        System.out.println(page.getContent());
    }
}

☞ CriteriaBuilder 方法对应关系

方法名称

SQL 对应关系

equle

filed = value

gt(greaterThan)

filed > value

lt(lessThan)

filed < value

ge(greaterThanOrEqualTo )

filed >= value

le(lessThanOrEqualTo)

filed <= value

notEqule

filed != value

like

filed like value

notLike

filed not like value