Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解。

快速上手

项目中的pom.xml、application.properties与 Chapter1 相同

实体类映射数据库表

user 实体类

@Entity
public class User implements Serializable { private static final long serialVersionUID = -390763540622907853L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private String name; private Integer age; private String email; // 省略构造器 set/get }

自定义简单查询

spring data 内部基础架构中有个根据方法名的查询生成器机制,对于在存储库的实体上构建约束查询很有用。该机制方法的前缀有find…By、read…By、query…By、count…By和get…By,从这些方法可以分析它的其余部分(实体里面的字段)。引入子句可以包含其他表达式,例如在Distinct要创建的查询上设置不同的标志。然而,第一个By作为分隔符来指示实际标准的开始。在一个非常基本的水平上,你可以定义实体性条件,并与它们串联(And和Or)。

注:此段来自 《Spring Data JPA 从入门到精通》。

继承 PagingAndSortingRepository

public interface UserPagingRepository extends PagingAndSortingRepository<User, Long> {
// 通过姓名查找
List<User> findByName(String name); // 通过姓名查找
List<User> queryByName(String name); // 通过姓名或者邮箱查找
List<User> findByNameOrEmail(String name,String email); // 计算某一个 age 的数量
int countByAge(int age);
}

测试类

路径:src/test/java/com/mtcarpenter/chapter2/repository/UserPagingRepositoryTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserPagingRepositoryTest { /**
* ⽇志对象
*/
private Logger logger = LoggerFactory.getLogger(UserPagingRepositoryTest.class); @Autowired
private UserPagingRepository userPagingRepository; @Before
public void save() {
logger.info("新增数据 result = {}", userPagingRepository.save(new User("小米", 9,"a@qq.com")));
logger.info("新增数据 result = {}", userPagingRepository.save(new User("张三", 16,"b@qq.com")));
logger.info("新增数据 result = {}", userPagingRepository.save(new User("三哥", 12,"c@qq.com")));
logger.info("新增数据 result = {}", userPagingRepository.save(new User("米二", 13,"e@qq.com")));
logger.info("新增数据 result = {}", userPagingRepository.save(new User("阿三", 12,"f@qq.com")));
logger.info("新增数据 result = {}", userPagingRepository.save(new User("张三", 12,"g@qq.com")));
logger.info("新增数据 result = {}", userPagingRepository.save(new User("米二", 8,"h@qq.com")));
} @Test
public void find(){
logger.info("通过姓名查找(findByName) result = {}", userPagingRepository.findByName("张三"));
logger.info("通过姓名查找(queryByName) result = {}", userPagingRepository.queryByName("张三"));
logger.info("通过姓名或者邮箱(findByNameOrEmail) 查找 result = {}", userPagingRepository.findByNameOrEmail("张三","f@qq.com"));
logger.info("通过某一个 age 的数量(countByAge) result = {}", userPagingRepository.countByAge(12));
} }

@Before会在@test之前运行。

输出日志:

Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.name as name4_0_ from user user0_ where user0_.name=?
通过姓名查找(findByName) result = [User{id=2, name='张三', age=16, email='b@qq.com'}, User{id=6, name='张三', age=12, email='g@qq.com'}] Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.name as name4_0_ from user user0_ where user0_.name=?
通过姓名查找(queryByName) result = [User{id=2, name='张三', age=16, email='b@qq.com'}, User{id=6, name='张三', age=12, email='g@qq.com'}] Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.name as name4_0_ from user user0_ where user0_.name=? or user0_.email=?
通过姓名或者邮箱(findByNameOrEmail) 查找 result = [User{id=2, name='张三', age=16, email='b@qq.com'}, User{id=5, name='阿三', age=12, email='f@qq.com'}, User{id=6, name='张三', age=12, email='g@qq.com'}] Hibernate: select count(user0_.id) as col_0_0_ from user user0_ where user0_.age=?
通过某一个 age 的数量(countByAge) result = 3

日志比较冗余删除了多余日志,从日志中我们可以发现 JPA 根据我们定义的接口方法自动解析成 SQL

方法中支持的关键字如下

关键字 示例 JPQL 表达式
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is, Equals findByFirstname,
findByFirstnameIs,
findByFirstnameEquals
… where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull, Null findByAge(Is)Null … 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 ages) … 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)

分页和排序

数据分页和排序在日常也是必不可少的,在 Spring Boot Jpa 中使用分页和排序,需要在Repository 接口的方法中,传入Pageable 实例 。

public interface UserPagingRepository extends PagingAndSortingRepository<User, Long> {
// 通过姓名条件查询
List<User> findByName(String name, Pageable pageable); }

测试方法

    @Test
public void pageAndSort(){
Sort sort = new Sort(Sort.Direction.DESC, "age");
int page = 0;
int size = 10;
Pageable pageable = PageRequest.of(page, size, sort);
logger.info("条件查询 result = {}", userPagingRepository.findByName("张三",pageable));
logger.info("---------------------------------");
logger.info("根据年龄排序 result = {}", userPagingRepository.findAll(sort));
}

测试结果

2020-02-29 17:02:37.431  INFO 48944 --- [           main] c.m.c.r.UserPagingRepositoryTest         : 条件查询 result = [User{id=2, name='张三', age=16, email='b@qq.com'}, User{id=6, name='张三', age=12, email='g@qq.com'}]
2020-02-29 17:02:37.431 INFO 48944 --- [ main] c.m.c.r.UserPagingRepositoryTest : ---------------------------------
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.name as name4_0_ from user user0_ order by user0_.age desc
2020-02-29 17:02:37.459 INFO 48944 --- [ main] c.m.c.r.UserPagingRepositoryTest : 根据年龄排序 result = [User{id=2, name='张三', age=16, email='b@qq.com'}, User{id=4, name='米二', age=13, email='e@qq.com'}, User{id=3, name='三哥', age=12, email='c@qq.com'}, User{id=5, name='阿三', age=12, email='f@qq.com'}, User{id=6, name='张三', age=12, email='g@qq.com'}, User{id=1, name='小米', age=9, email='a@qq.com'}, User{id=7, name='米二', age=8, email='h@qq.com'}]

复杂条件查询

前面演示了 CrudRepositoryPagingAndSortingRepository ,下面通过继承 JpaRepositoryJpaSpecificationExecutor 操作更复杂的语句。

JpaSpecificationExecutor 是 JPA 2.0 提供的Criteria API,可以用于动态生成query。Spring Data JPA 支持 Criteria 查询,可以很方便地使用,足以应付工作中的所有复杂查询的情况了,可以对 JPA 实现最大限度的扩展。《spring data Jpa 从入门到精通》

public interface JpaSpecificationExecutor<T> {
// 根据 Specification 条件查询单个对象
Optional<T> findOne(@Nullable Specification<T> var1);
// 根据 Specification 条件查询 返回 List 结果
List<T> findAll(@Nullable Specification<T> var1);
// 根据 Specification 条件 和 分页条件查询
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
// 根据 Specification 条件 和 排序条件查询 返回 List 结果
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
// 根据 Specification 条件查询数量
long count(@Nullable Specification<T> var1);
}

UserJpaRepository 数据层接口

public interface UserJpaRepository extends JpaRepository<User,Long>, JpaSpecificationExecutor {
}

测试类

路径:src/test/java/com/mtcarpenter/chapter2/repository/UserJpaRepositoryTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserJpaRepositoryTest { @Test
public void specification() {
Specification specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
// like 模糊查询 , root.get("name") 属性名 "%三%" 为三
Predicate p1 = cb.like(root.get("name"), "%三%");
// greaterThan 表示 age 大于 10
Predicate p2 = cb.greaterThan(root.get("age"), 10);
// cb.and(p1, p2) ,and 则表示 p1 和 p2 并且关系,除了 and 还有or, not等。点击 CriteriaBuilder 可进行查看
return cb.and(p1, p2);
}
}; } @Test
public void ConditionalQuery() {
int page = 0;
int size = 10;
Pageable pageable = PageRequest.of(page, size);
// 模拟传入的条件
User user = new User("三", 10, "b@qq.com");
Specification specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
// 判断传入的值是否为空
if (!"".equals(user.getName())) {
predicates.add(cb.like(root.get("name"), "%" + user.getName() + "%"));
}
// 判断年龄是否为空
if (user.getAge() != null) {
predicates.add(cb.greaterThan(root.get("age"), user.getAge()));
} return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
Page result = userJpaRepository.findAll(specification, pageable);
logger.info("条件查询 result = {}", result.getContent());
} }

specification() 方法更容易理解,如果看懂了此方法,有利于更了解ConditionalQuery() 方法。ConditionalQuery() 方法这种模式也是在实际开发中,使用的频率比较高的方法。

JpaSpecificationExecutor 通过 CriteriaQuery 几乎可以实现任何逻辑了。

本章代码

【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询的更多相关文章

  1. spring JPA分页排序条件查询

    @RequestMapping("/listByPage") public Page<Production> listByPage(int page, int size ...

  2. Spring Data JPA,一种动态条件查询的写法

    我们在使用SpringData JPA框架时,进行条件查询,如果是固定条件的查询,我们可以使用符合框架规则的自定义方法以及@Query注解实现. 如果是查询条件是动态的,框架也提供了查询接口. Jpa ...

  3. jpa中使用Query判断条件查询

    jpa中使用Query判断条件查询 @Query(value = " select m.* from mining_area as m " + " where 1 = 1 ...

  4. 通过带参数的Sql语句来实现模糊查询(多条件查询)

    #region 通过带参数的Sql语句来实现模糊查询(多条件查询) StringBuilder sb = new StringBuilder("select * from books&quo ...

  5. lucene 查询+分页+排序

    lucene 查询+分页+排序 1.定义一个工厂类 LuceneFactory 1 import java.io.IOException; 2 3 import org.apache.lucene.a ...

  6. ormlite 在android中 排序 条件查询

    ormlite 在android中 排序 条件查询 all = dao.queryBuilder().orderBy("Id", true).where().eq("Ty ...

  7. MyBatis关联查询、多条件查询

    MyBatis关联查询.多条件查询 1.一对一查询 任务需求; 根据班级的信息查询出教师的相关信息 1.数据库表的设计 班级表: 教师表: 2.实体类的设计 班级表: public class Cla ...

  8. Hibernate结合JPA编写通用泛型多条件查询

    项目中使用Hibernate和JPA对数据库对象进行实例化,但是生成的方法不支持多条件查询.而如果针对每一个数据库对象进行多条件查询编码,则会变得很麻烦,而且一旦以后发生表结构发生变化,这些方法可能还 ...

  9. ajxa分页+多条件查询

    操作日志数据库表: 主页面: <script src="../fzl/jquery-1.11.2.min.js"></script> <script ...

  10. 【Oracle】曾经的Oracle学习笔记(4-7)多表联合查询,子查询,动态条件查询

    一.多表联合查询 二.子查询 三.动态条件查询 LESSON 4 Displaying Data from Multiple Tables------------------------------- ...

随机推荐

  1. Dangal 观影感受,(摘录)

    ===================================================================================== 引用: https://ww ...

  2. 使用VSCode调试Javascript的三种方式

    Code Runner 在应用商店中搜索Code Runner插件进行安装. 选中你要执行的Javascript脚本,右键选择Run Code,利用Console.log在下方的输出窗口里可以看到输出 ...

  3. 康耐视软件VisionPro-max-u与VisionPro-plus-u的区别

    康耐视软件VisionPro-max-u与VisionPro-plus-u的区别 1.VisionPro-plus-u为基础版可以直接运用该软件包的算法,拖拽式的窗口程序 2.VisionPro-ma ...

  4. 关于AI行业创业的6个问题

    第一个问题:互联网 vs 人工智能 首先如果今天大家选择创业,我建议更应该关注人工智能,而非互联网.为什么这么讲? 1. 互联网的流量红利已经消失: 以PC来说,全球PC出货量连续5年下滑.大家知道国 ...

  5. python3下应用pymysql(第二卷)

    上一卷讲述的是单条插入数据,现在要多条插入数据: 随意定义了一批数据 去数据库查询一下: 下面试一下查询语句: 获取游标里的数据,结果如下: 下面更改下返回数据类型,如果想用字典类型: 结果如下: 在 ...

  6. CF-1117C-Magic Ship

    二分 C - Magic Ship GNU C++11 Accepted 31 ms 1700 KB #include "bits/stdc++.h" using namespac ...

  7. CF-1102E-Monotonic Renumeration

    比较可惜昨天比赛的时候时间不够了,在比赛结束之后五分钟找出了bug提交通过了.然并软: 首先这题说b数组的后一项要么等于前一项,要么等于前一项加一,而且如果a[i] == a[j] ,那么b[i] = ...

  8. 接口测试-chap5-使用正则表达式提取响应数据

    1.导入相关库 import re 2.re.findall(r"前(.+?)后", 匹配源) 3.前:表示要匹配的文本左边的内容 4.后:表示要匹配的文本右边的内容 5.它的返回 ...

  9. 吴裕雄--天生自然python学习笔记:Python3 正则表达式

    Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. re 模块使 Python 语言拥有全部的正则表达式功能. compile 函数根据一个模式字符串和可选的标志参 ...

  10. Nginx笔记总结三:内核参数优化

    net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 1800 net.ipv4.ip_conntrack_max = 16777216 ...