前言

之前在学习 SpringBoot 框架的时候,使用到了 SpringData JPA,但是当时只是简单的查询,没有用到稍微复杂的查询。

JPA 的 JPQL 语法规则对于简单的查询实属利器,大大加快了开发速度。不久前,在公司将用户推荐功能单独抽取出为一个独立项目,由于公司一直沿用的底层框架太老,只能使用 JDK1.6,JDK 1.9都出来了,实在不能忍😅,果断引入了 SpringData JPA。

然后最近公司其他同事接手了该项目,但是不太了解 SpringData JPA 的使用,于是有了此文,不会就可以直接让他看本篇博客了哈哈。

环境准备

这里不讲解 SpringData JPA 与框架的整合,只讲解 JPA 语法的使用

Entity 实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity // 表示为一个实体类
@Table("employee") // 表名
public class Employee {

@Id //主键标识注解
@GeneratedValue // 主键生成方式
private Integer id;

private String name;

private Integer age;

//Getter/Setter省略
}

Repository 接口

1
2
3
4
// 继承 JpaRepository 接口,第一个参数为查询的实体类,第二个为实体类的主键数据类型
public interface EmployeeRepository extends JpaRepository<Employee, Integer>{

}

插入测试数据

1
2
3
4
5
6
7
8
9
@Test
public void testAdd() throws Exception {
for (int i = 0; i < 100; i++) {
Employee employee = new Employee();
employee.setAge(i);
employee.setName("test" + i);
employeeRepository.save(employee);
}
}

JPA 查询语法讲解

使用 JPQL 进行查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 新增
employeeRepository.save(employee);

// where name = ?1
employeeRepository.findByName("test1")

// where name = ?1 and age = ?2
employeeRepository.findByNameAndAge("test1", 20);

// where age between ?1 and ?2 【包含头尾】
List<Employee> byAgeBetween = employeeRepository.findByAgeBetween(10, 16);

// where age < ?1
List<Employee> list = employeeRepository.findByAgeLessThan(10);

// where age > ?1
List<Employee> list = employeeRepository.findByAgeGreaterThan(90);

// where name is null 【不包含为空字符串的数据】
List<Employee> list = employeeRepository.findByNameIsNull();

// where name like "test9%" 以test9为开头的name
List<Employee> list = employeeRepository.findByNameLike("test9%");
或者
List<Employee> employees = employeeRepository.findByNameStartingWith("test")

// where name like "test_" 以test开头,且后面只模糊匹配一位
List<Employee> list2 = employeeRepository.findByNameLike("test_");

// where name like "%6" 模糊匹配以6结尾的
List<Employee> employees = employeeRepository.findByNameEndingWith(6)

// where name in (?1, ?2)
List<String> names = Arrays.asList("test1", "test2");
List<Employee> employees = employeeRepository.findByNameIn(names);

// where age <> ?1
List<Employee> employees = employeeRepository.findByAgeNot(99);

// where name = ?1 order by age desc
List<Employee> findByNameOrderByAgeDesc("test");

使用自定义 Sql 以及 原生 Sql 查询

1
2
3
4
5
6
7
8
9
10
11
12
13
/** EmployeeRepository.java 添加方法 */

// 根据姓名与年龄查找,[通过占位符获取参数值]
@Query("select o from Employee o where name = ?1 and age = ?2")
List<Employee> queryEmployeeByParams(String name, Integer age);

// 根据姓名与年龄查找,[通过命名参数获取参数值],必须使用 @Param 注解
@Query("select o from Employee o where name = :name and age = :age")
List<Employee> queryEmployeeByParams2(@Param("name") String name, @Param("age") Integer age);

// 原生SQL,与上面不同的是,上面使用的是对象名称以及对象属性名称,Native SQL使用数据库表名以及字段名
@Query(nativeQuery = true, value = "select * from employee where name = :name and age = :age")
List<Employee> queryEmployeeByParams3(@Param("name") String name, @Param("age") Integer age);

JPA 更新操作

1
2
3
4
5
/** 需要搭配使用 @Query@Modifying@Transactional 注解使用*/

@Modifying
@Query("update Employee o set o.age = ?2 where o.id = ?1")
Integer updateAge(Integer id, Integer age);

在 Service 层调用

1
2
3
4
5
6
7
@Autowired
private EmployeeRepository employeeRepository;

@Transactional // 必须开启事务
public void update(Integer id, Integer age) {
employeeRepository.update(id, age);
}

分页查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// EmployeeRepository 接口定义
Page<Employee> findByNameStartingWith(String name, Pageable pageable);

// 测试类 EmployeeRepositoryTest.java
// 普通分页查询
@Test
public void testFindByNameStartingWith() {
// 注意 page 从 0 开始
Pageable request = new PageRequest(0, 10);
Page<Employee> result = employeeRepository.findByNameStartingWith("test", request);
for (Employee employee : result.getContent()) {
System.out.println(employee);
}
}

// 带排序条件的分页查询
@Test
public void testFindByNameStartingWith() {
Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id");
Sort sort = new Sort(order);
Pageable request = new PageRequest(0, 10, sort);
Page<Employee> result = employeeRepository.findByNameStartingWith("test", request);
for (Employee employee : result.getContent()) {
System.out.println(employee);
}
}

动态 SQL 查询

在 Java 开发中,动态 SQL 是必不可少的,JPA 也可以实现,Repository 多继承一个接口 JpaSpecificationExecutor 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 修改之前的 EmployeeRepository, 使其多继承 JpaSpecificationExecutor 接口
public interface EmployeeRepository extends JpaRepository<Employee, Integer>, JpaSpecificationExecutor<Employee>{

// ......
}


Pageable request = new PageRequest(0, 10);
Specification<Employee> specification = new Specification<Employee>() {
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Integer> path = root.get("age");
return cb.gt(path, 50);
}
};
Page<Employee> all = employeeRepository.findAll(specification, request);

后记

这里感谢一下慕课网,快速入门多亏了 imooc 上的课程。

参考课程: