【DB系列】JPA之query使用姿势详解之基础篇

文章目录
  1. I. 环境准备
    1. 1. 表准备
    2. 2. 项目配置
    3. 3. 数据准备
  2. II. Query基本使用姿势
    1. 1. 表关联POJO
    2. 2. Repository API声明
    3. 3. 使用姿势
      1. a. 根据id查询
      2. b. 根据字段查询
      3. c. like查询
      4. d. in查询
      5. e. 比较查询
      6. f. 排序
      7. g. 分页查询
    4. 4. 小结
  3. II. 其他
    1. 0. 源码&相关博文
    2. 1. 一灰灰Blog

前面的几篇文章分别介绍了CURD中的增删改,接下来进入最最常见的查询篇,看一下使用jpa进行db的记录查询时,可以怎么玩

本篇将介绍一些基础的查询使用姿势,主要包括根据字段查询,and/or/in/like/between 语句,数字比较,排序以及分页

I. 环境准备

在开始之前,当然得先准备好基础环境,如安装测试使用mysql,创建SpringBoot项目工程,设置好配置信息等,关于搭建项目的详情可以参考前一篇文章

下面简单的看一下演示添加记录的过程中,需要的配置

1. 表准备

沿用前一篇的表,结构如下

1
2
3
4
5
6
7
8
9
10
CREATE TABLE `money` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
`money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

2. 项目配置

配置信息,与之前有一点点区别,我们新增了更详细的日志打印;本篇主要目标集中在添加记录的使用姿势,对于配置说明,后面单独进行说明

1
2
3
4
5
6
7
8
9
10
11
## DataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
## jpa相关配置
spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

3. 数据准备

数据修改嘛,所以我们先向表里面插入两条数据,用于后面的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
INSERT INTO `money` (`id`, `name`, `money`, `is_deleted`, `create_at`, `update_at`)
VALUES
(1, '一灰灰blog', 100, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(2, '一灰灰2', 200, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(3, '一灰灰3', 300, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(4, '一灰灰4', 400, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(5, '一灰灰5', 500, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(6, 'Batch 一灰灰blog', 100, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(7, 'Batch 一灰灰blog 2', 100, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(8, 'Batch 一灰灰 3', 200, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(9, 'Batch 一灰灰 4', 200, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(10, 'batch 一灰灰5', 1498, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:58'),
(11, 'batch 一灰灰6', 1498, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:58'),
(12, 'batch 一灰灰7', 400, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40'),
(13, 'batch 一灰灰8', 400, 0, '2019-04-18 17:01:40', '2019-04-18 17:01:40');

db

II. Query基本使用姿势

下面进入简单的查询操作姿势介绍,单表的简单and/or/in/compare查询方式

1. 表关联POJO

查询返回的记录与一个实体类POJO进行绑定,借助前面的分析结果,如下

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
@Data
@DynamicUpdate
@DynamicInsert
@Entity
@Table(name = "money")
public class MoneyPO {
@Id
// 如果是auto,则会报异常 Table 'mysql.hibernate_sequence' doesn't exist
// @GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;

@Column(name = "name")
private String name;

@Column(name = "money")
private Long money;

@Column(name = "is_deleted")
private Byte isDeleted;

@Column(name = "create_at")
@CreatedDate
private Timestamp createAt;

@Column(name = "update_at")
@CreatedDate
private Timestamp updateAt;

}

上面类中的几个注解,说明如下

  • @Data 属于lombok注解,与jpa无关,自动生成getter/setter/equals/hashcode/tostring等方法
  • @Entity, @Table jpa注解,表示这个类与db的表关联,具体匹配的是表 money
  • @Id @GeneratedValue 作用与自增主键
  • @Column表明这个属性与表中的某列对应
  • @CreateDate根据当前时间来生成默认的时间戳

2. Repository API声明

接下来我们新建一个api继承自CurdRepository,然后通过这个api来与数据库打交道,后面会在这个类中添加较多的查询方法

1
2
public interface MoneyBaseQueryRepository extends CrudRepository<MoneyPO, Integer> {
}

3. 使用姿势

a. 根据id查询

CrudRepository已经提供的功能,根据主键id进行查询,对于使用者而言,没有什么需要额外操作的,直接访问即可

1
2
3
4
5
private void queryById() {
// 根据主键查询,直接使用接口即可
Optional<MoneyPO> res = moneyCurdRepository.findById(1);
System.out.println("queryById return: " + res.get());
}

b. 根据字段查询

除了根据主键查询,实际的业务场景中,根据某个字段进行查询的case,简直不要更多,在jpa中可以怎么做呢?

  • Repository接口中声明一个方法,命名规则为
  • findByXXX 或者 queryByXXX (注意这里的xxx用POJO中的成员名替换,表示根据这个成员进行查询)

一个简单的case,如果我希望实现根据name进行查询,那么在MoneyBaseQueryRepository中添加下面两个方法中的任意一个都可以

1
2
3
4
5
6
7
8
9
/**
* 根据用户名查询
*
* @param name
* @return
*/
List<MoneyPO> findByName(String name);

List<MoneyPO> queryByName(String name);

如果需要多个成员的查询呢?也简单,形如findByXxxAndYyyy相当于sql中的where xxxx=? and yyy=?

如我们也可以增加下面两个方法(一个and、一个or查询)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 根据用户名 + money查询
*
* @param name
* @param money
* @return
*/
List<MoneyPO> findByNameAndMoney(String name, Long money);


/**
* 根据用户名 or id查询
*
* @param name
* @param id
* @return
*/
List<MoneyPO> findByNameOrId(String name, Integer id);

一个简单的测试case可以如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void queryByField() {
// 根据内部成员进行查询,需要自己定义新的接口
String name = "一灰灰blog";
Iterable<MoneyPO> res = moneyCurdRepository.findByName(name);
System.out.println("findByName return: " + res);

res = moneyCurdRepository.queryByName(name);
System.out.println("queryByName return: " + res);

Long money = 100L;
res = moneyCurdRepository.findByNameAndMoney(name, money);
System.out.println("findByNameAndMoney return: " + res);

Integer id = 5;
res = moneyCurdRepository.findByNameOrId(name, id);
System.out.println("findByNameOrId return: " + res);
}

执行之后输出结果如下,下面也包括了对应的sql,便于理解

1
2
3
4
5
6
7
8
9
10
11
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.name=?
findByName return: [MoneyPO(id=1, name=一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
-------- 人工拆分 -----------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.name=?
queryByName return: [MoneyPO(id=1, name=一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
-------- 人工拆分 -----------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.name=? and moneypo0_.money=?
findByNameAndMoney return: [MoneyPO(id=1, name=一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
-------- 人工拆分 -----------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.name=? or moneypo0_.id=?
findByNameOrId return: [MoneyPO(id=1, name=一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=5, name=一灰灰5, money=500, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]

c. like查询

上面的查询方式为等值查询,当在sql中除了等值查询(即=查询)之外,还有各种比较查询,不等查询以及like语句,在jpa中也比较简单,在repository定义的方法名,加一个like即可

1
2
3
4
5
6
7
/**
* like查询
*
* @param name
* @return
*/
List<MoneyPO> findByNameLike(String name);

使用的时候,需要稍微注意一下,根据实际情况决定要不要加上 ‘%’

1
2
3
4
5
6
private void queryByLike() {
// like 语句查询
String name = "一灰灰%";
Iterable<MoneyPO> res = moneyCurdRepository.findByNameLike(name);
System.out.println("findByName like: " + res);
}

输出结果为

1
2
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.name like ?
findByName like: [MoneyPO(id=1, name=一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=2, name=一灰灰2, money=200, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=3, name=一灰灰3, money=300, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=4, name=一灰灰4, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=5, name=一灰灰5, money=500, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]

d. in查询

对于in查询,CurdRepository提供了根据主键id的查询方式,直接调用findAllById即可,如果是其他的,可以通过声明一个接口的方式来支持

1
2
3
4
5
6
7
/**
* in查询
*
* @param moneys
* @return
*/
List<MoneyPO> findByMoneyIn(List<Long> moneys);

测试case如下

1
2
3
4
5
6
7
// in 查询
List<Integer> ids = Arrays.asList(1, 2, 3);
Iterable<MoneyPO> res = moneyCurdRepository.findAllById(ids);
System.out.println("findByIds return: " + res);

res = moneyCurdRepository.findByMoneyIn(Arrays.asList(400L, 300L));
System.out.println("findByMoneyIn return: " + res);

输出结果

1
2
3
4
5
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id in (? , ? , ?)
findByIds return: [MoneyPO(id=1, name=一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=2, name=一灰灰2, money=200, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=3, name=一灰灰3, money=300, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
------ 手动拆分 ----------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.money in (? , ?)
findByMoneyIn return: [MoneyPO(id=3, name=一灰灰3, money=300, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=4, name=一灰灰4, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=12, name=batch 一灰灰7, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=13, name=batch 一灰灰8, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]

e. 比较查询

数字的比较查询,比如大于等于,大于,小于,小于等于,between,下面的三个方法声明,应该能直观表示这种方式可以如何写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 查询大于or等于指定id的所有记录
*
* @param id
* @return
*/
List<MoneyPO> findByIdGreaterThanEqual(Integer id);

/**
* 查询小于or等于指定id的所有记录
*
* @param id
* @return
*/
List<MoneyPO> findByIdLessThanEqual(Integer id);

/**
* between查询
*
* @param low
* @param high
* @return
*/
List<MoneyPO> findByIdIsBetween(Integer low, Integer high);

下面是简单的映射关系

  • > : xxGreaterThan
  • >=: xxGreaterThanEqual
  • <: xxLessThan
  • <=: xxLessThanEqual
  • !=: xxNot
  • between a and b : xxIsBetween

测试case如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void queryByCompare() {
Integer id1 = 3;
Iterable<MoneyPO> res = moneyCurdRepository.findByIdLessThanEqual(id1);
System.out.println("findByIdLessThan 3 return: " + res);


Integer id2 = 10;
res = moneyCurdRepository.findByIdGreaterThanEqual(id2);
System.out.println("findByIdGreaterThan 10 return: " + res);

id1 = 4;
id2 = 6;
res = moneyCurdRepository.findByIdIsBetween(id1, id2);
System.out.println("findByIdsWBetween 3, 10 return: " + res);
}

输出结果为

1
2
3
4
5
6
7
8
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id<=?
findByIdLessThan 3 return: [MoneyPO(id=1, name=一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=2, name=一灰灰2, money=200, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=3, name=一灰灰3, money=300, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
------ 手动拆分 ----------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>=?
findByIdGreaterThan 10 return: [MoneyPO(id=10, name=batch 一灰灰5, money=1498, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:58.0), MoneyPO(id=11, name=batch 一灰灰6, money=1498, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:58.0), MoneyPO(id=12, name=batch 一灰灰7, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=13, name=batch 一灰灰8, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
------ 手动拆分 ----------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id between ? and ?
findByIdsWBetween 3, 10 return: [MoneyPO(id=4, name=一灰灰4, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=5, name=一灰灰5, money=500, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=6, name=Batch 一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]

f. 排序

排序也属于基本查询的case了,jpa的实现中,通过加上OrderByXxxAsc/Desc的方式来决定根据什么进行升序or降序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 根据money查询,并将最终的结果根据id进行倒排
*
* @param money
* @return
*/
List<MoneyPO> findByMoneyOrderByIdDesc(Long money);

/**
* 根据多个条件进行排序
*
* @param id
* @return
*/
List<MoneyPO> queryByIdGreaterThanEqualOrderByMoneyDescIdAsc(Integer id);

在根据多个列进行排序时,需要注意的是不能写多个 OrderBy 而是直接在OrderBy后面加上对应的xxxAscyyyDesc

测试代码如

1
2
3
4
5
6
7
8
9
10
private void queryWithSort() {
// 排序
Long money = 400L;
Iterable<MoneyPO> res = moneyCurdRepository.findByMoneyOrderByIdDesc(money);
System.out.println("findByMoneyAndOrderByIdDesc return: " + res);

Integer startId = 7;
res = moneyCurdRepository.queryByIdGreaterThanEqualOrderByMoneyDescIdAsc(startId);
System.out.println("queryByIdGreaterThanEqualOrderByMoneyDescIdAsc return: " + res);
}

输出结果如下

1
2
3
4
5
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.money=? order by moneypo0_.id desc
findByMoneyAndOrderByIdDesc return: [MoneyPO(id=13, name=batch 一灰灰8, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=12, name=batch 一灰灰7, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=4, name=一灰灰4, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
------------- 人工拆分 --------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>=? order by moneypo0_.money desc, moneypo0_.id asc
queryByIdGreaterThanEqualOrderByMoneyDescIdAsc return: [MoneyPO(id=10, name=batch 一灰灰5, money=1498, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:58.0), MoneyPO(id=11, name=batch 一灰灰6, money=1498, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:58.0), MoneyPO(id=12, name=batch 一灰灰7, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=13, name=batch 一灰灰8, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=8, name=Batch 一灰灰 3, money=200, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=9, name=Batch 一灰灰 4, money=200, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=7, name=Batch 一灰灰blog 2, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]

g. 分页查询

分页有两种方式,一个是查询最大的多少条数据,一个是正常的limit/offset方式,下面是一个简单的实例demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 分页查询,获取前面三个数据
*
* @param id
* @return
*/
List<MoneyPO> findTop3ByIdGreaterThan(Integer id);

/**
* 分页查询
*
* @param id
* @param pageable page 从0开始表示查询第0页,即返回size个正好>id数量的数据
* @return
*/
List<MoneyPO> findByIdGreaterThan(Integer id, Pageable pageable);

对于分页而言,通过传入参数Pageable来表明即可

测试case如

1
2
3
4
5
6
7
8
9
private void queryWithPageSize() {
// 分页查询
Iterable<MoneyPO> res = moneyCurdRepository.findTop3ByIdGreaterThan(3);
System.out.println("findTop3ByIdGreaterThan 3 return: " + res);

// id>3,第2页,每页3条,如果id递增时,则返回的第一条id=4 + 2 * 3 = 10
res = moneyCurdRepository.findByIdGreaterThan(3, PageRequest.of(2, 3));
System.out.println("findByIdGreaterThan 3 pageIndex 2 size 3 return: " + res);
}

输出结果为

1
2
3
4
5
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>? limit ?
findTop3ByIdGreaterThan 3 return: [MoneyPO(id=4, name=一灰灰4, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=5, name=一灰灰5, money=500, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0), MoneyPO(id=6, name=Batch 一灰灰blog, money=100, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]
---------- 人工拆分 ------------
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>? limit ?, ?
findByIdGreaterThan 3 pageIndex 2 size 3 return: [MoneyPO(id=10, name=batch 一灰灰5, money=1498, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:58.0), MoneyPO(id=11, name=batch 一灰灰6, money=1498, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:58.0), MoneyPO(id=12, name=batch 一灰灰7, money=400, isDeleted=0, createAt=2019-04-18 17:01:40.0, updateAt=2019-04-18 17:01:40.0)]

4. 小结

主要介绍了jpa的查询的最基本使用方式,主要是根据规则定义方法名的方式来实现sql的效果, 下表示一个简单的对比小结

方法名 说明 等效sql
findByXxx 表示根据列Xxx等于传参构建sql where xxx= ?
findByXxxAndYyy 根据多个列进行查询 where xxx=? and yyy=?
findByXxxOrYyy 根据多个列实现or查询 where xxx=? or yyy=?
findByXxxLike like查询,需要注意查询条件中加% where xxx like
findByXxxIn in查询 where Xxx in ()
findByXxxGreaterThan 大于 where xxx > ?
findByXxxGreaterThanEqual 大于等于 where xxx >= ?
findByXxxLessThan 小于 where xxx < ?
findByXxxLessThanEqual 小于等于 where xxx <= ?
findByXxxNot 不等于 where xxx != ?
findByXxxIsBetween between查询 where xxx between ? and ?
OrderByXxxDesc 排序 order by xxx desc
topN 分页,表示获取最前面的n条 limit n

此外还有一个分页的方式是传参Pageable,来指定具体的分页

我们常见的查询操作中,除了上面的一些case之外,还有一些是我们没有提到的,如下面的一些使用姿势,则会在后面的文章中引入

  • group by
  • distinct
  • join
  • 各种函数的支持(sum,max,min,avg…)
  • 查询部分表中部分字段时
  • 统计查询

II. 其他

0. 源码&相关博文

源码

相关博文

1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

一灰灰blog


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到