乐趣区

SpringBoot系列教程JPA之query使用姿势详解之基础篇

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

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

<!– more –>

I. 环境准备

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

  • 190612-SpringBoot 系列教程 JPA 之基础环境搭建

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

1. 表准备

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

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. 项目配置

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

## 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. 数据准备

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

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');

II. Query 基本使用姿势

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

1. 表关联 POJO

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

@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 来与数据库打交道,后面会在这个类中添加较多的查询方法

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

3. 使用姿势

a. 根据 id 查询

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

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 中添加下面两个方法中的任意一个都可以

/**
 * 根据用户名查询
 *
 * @param name
 * @return
 */
List<MoneyPO> findByName(String name);

List<MoneyPO> queryByName(String name);

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

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

/**
 * 根据用户名 + 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 可以如下

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,便于理解

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 即可

/**
 * like 查询
 *
 * @param name
 * @return
 */
List<MoneyPO> findByNameLike(String name);

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

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

输出结果为

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 即可,如果是其他的,可以通过声明一个接口的方式来支持

/**
 * in 查询
 *
 * @param moneys
 * @return
 */
List<MoneyPO> findByMoneyIn(List<Long> moneys);

测试 case 如下

// 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);

输出结果

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,下面的三个方法声明,应该能直观表示这种方式可以如何写

/**
 * 查询大于 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 如下

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);
}

输出结果为

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 降序

/**
 * 根据 money 查询,并将最终的结果根据 id 进行倒排
 *
 * @param money
 * @return
 */
List<MoneyPO> findByMoneyOrderByIdDesc(Long money);

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

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

测试代码如

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);
}

输出结果如下

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

/**
* 分页查询,获取前面三个数据
*
* @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 如

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);
}

输出结果为

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. 源码 & 相关博文

源码

  • 工程:https://github.com/liuyueyi/spring-boot-demo
  • module: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/102-jpa

相关博文

  • mysql 之锁与事务详解
  • Spring 学习之事务的使用姿势
  • Spring 学习之事务管理与传播属性
  • 190612-SpringBoot 系列教程 JPA 之基础环境搭建
  • 190614-SpringBoot 系列教程 JPA 之新增记录使用姿势
  • 190623-SpringBoot 系列教程 JPA 之 update 使用姿势
  • 190702-SpringBoot 系列教程 JPA 之 delete 使用姿势详解

1. 一灰灰 Blog

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

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

  • 一灰灰 Blog 个人博客 https://blog.hhui.top
  • 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top

退出移动版