JpaRepository 与 JpaSpecificationExecutor

Stella981
• 阅读 176

1.JpaRepository支持接口规范方法名查询。意思是如果在接口中定义的查询方法符合它的命名规则,就可以不用写实现,目前支持的关键字如下。

Keyword

Sample

JPQL snippet

IsNotNull

findByAgeNotNull

...  where x.age not null

Like

findByNameLike

...  where x.name like ?1

NotLike

findByNameNotLike

...  where x.name not like ?1

StartingWith

findByNameStartingWith

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

EndingWith

findByNameEndingWith

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

Containing

findByNameContaining

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

OrderBy

findByAgeOrderByName

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

Not

findByNameNot

...  where x.name <> ?1

In

findByAgeIn

...  where x.age in ?1

NotIn

findByAgeNotIn

...  where x.age not in ?1

True

findByActiveTrue

...  where x.avtive = true

Flase

findByActiveFalse

...  where x.active = false

And 

findByNameAndAge

...  where x.name = ?1 and x.age = ?2

Or

findByNameOrAge

...  where x.name = ?1 or x.age = ?2

Between

findBtAgeBetween

...  where x.age between ?1 and ?2

LessThan

findByAgeLessThan

...  where x.age  <  ?1

GreaterThan

findByAgeGreaterThan

...  where x.age > ?1

After/Before

...

...

IsNull

findByAgeIsNull

...  where x.age is null

2.JpaRepository相关查询功能

a.Spring DataJPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。

b.假如创建如下的查询:findByUserDepUuid(),框架在解析该方法时,首先剔除

findBy,然后对剩下的属性进行解析,假设查询实体为Doc。

1:先判断userDepUuid (根据POJO规范,首字母变为小写)是否为查询实体的一个

属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;

2:从右往左截取第一个大写字母开头的字符串此处为Uuid),然后检查剩下的字符串是

否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,

则重复第二步,继续从右往左截取;最后假设user为查询实体的一个属性;

3:接着处理剩下部分(DepUuid),先判断user所对应的类型是否有depUuid属性,如

果有,则表示该方法最终是根据“Doc.user.depUuid” 的取值进行查询;否则继

续按照步骤2的规则从右往左截取,最终表示根据“Doc.user.dep.uuid” 的值进

行查询。

4:可能会存在一种特殊情况,比如Doc包含一个user的属性,也有一个userDep 属

性,此时会存在混淆。可以明确在属性之间加上"_"以显式表达意图,比如

"findByUser_DepUuid()"或者"findByUserDep_uuid()"

c.特殊的参数: 还可以直接在方法的参数上加入分页或排序的参数,比如:

PagefindByName(String name, Pageable pageable);

ListfindByName(String name, Sort sort);

d.也可以使用JPA的NamedQueries,方法如下:

1:在实体类上使用@NamedQuery,示例如下:

@NamedQuery(name ="UserModel.findByAge",query = "select o from UserModel

o where o.age >=?1")

2:在自己实现的DAO的Repository接口里面定义一个同名的方法,示例如下:

publicList findByAge(int age);

3:然后就可以使用了,Spring会先找是否有同名的NamedQuery,如果有,那么就不

会按照接口定义的方法来解析。

e.还可以使用@Query来指定本地查询,只要设置nativeQuery为true,比如:

@Query(value="select* from tbl_user where name like %?1" ,nativeQuery=true)

publicList findByUuidOrAge(String name);

注意:当前版本的本地查询不支持翻页和动态的排序

f.使用命名化参数,使用@Param即可,比如:

@Query(value="selecto from UserModel o where o.name like %:nn")

publicList findByUuidOrAge(@Param("nn") String name);

g.同样支持更新类的Query语句,添加@Modifying即可,比如:

@Modifying

@Query(value="updateUserModel o set o.name=:newName where o.name like %:nn")

public intfindByUuidOrAge(@Param("nn") String name,@Param("newName")String

newName);

注意:

1:方法的返回值应该是int,表示更新语句所影响的行数

2:在调用的地方必须加事务,没有事务不能正常执行

f.创建查询的顺序

Spring Data JPA在为接口创建代理对象时,如果发现同时存在多种上述

情况可用,它该优先采用哪种策略呢?

jpa:repositories提供了query-lookup-strategy 属性,用以指定查

找的顺序。它有如下三个取值:

1:create-if-not-found:如果方法通过@Query指定了查询语句,则使用该语句实现

查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该

命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。这是querylookup-

strategy 属性的默认值

2:create:通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过

@Query指定的查询语句,都将会被忽略

3:use-declared-query:如果方法通过@Query指定了查询语句,则使用该语句实现

查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该

命名查询;如果两者都没有找到,则抛出异常

spring data jpa 通过创建方法名来做查询,只能做简单的查询,那如果我们要做复杂一些的查询呢,多条件分页怎么办,这里,spring data jpa为我们提供了JpaSpecificationExecutor接口,只要简单实现toPredicate方法就可以实现复杂的查询

1.首先让我们的接口继承于JpaSpecificationExecutor

  1. public interface TaskDao extends JpaSpecificationExecutor<Task>{

  2. }

  • 1
  • 2
  • 3

2.JpaSpecificationExecutor提供了以下接口

  1. public interface JpaSpecificationExecutor<T> {

  2. T findOne(Specification<T> spec);

  3. List<T> findAll(Specification<T> spec);

  4. Page<T> findAll(Specification<T> spec, Pageable pageable);

  5. List<T> findAll(Specification<T> spec, Sort sort);

  6. long count(Specification<T> spec);

  7. }

其中Specification就是需要我们传进去的参数,它是一个接口

  1. public interface Specification<T> {

  2. Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);

  3. }

提供唯一的一个方法toPredicate,我们只要按照JPA 2.0 criteria api写好查询条件就可以了,关于JPA 2.0 criteria api的介绍和使用,欢迎参考 
http://blog.csdn.net/dracotianlong/article/details/28445725 
http://developer.51cto.com/art/200911/162722.htm

2.接下来我们在service bean

  1. @Service

  2. public class TaskService {

  3. @Autowired TaskDao taskDao ;

  4. /**

  5. * 复杂查询测试

  6. * @param page

  7. * @param size

  8. * @return

  9. */

  10. public Page<Task> findBySepc(int page, int size){

  11. PageRequest pageReq = this.buildPageRequest(page, size);

  12. Page<Task> tasks = this.taskDao.findAll(new MySpec(), pageReq);

  13. return tasks;

  14. }

  15. /**

  16. * 建立分页排序请求

  17. * @param page

  18. * @param size

  19. * @return

  20. */

  21. private PageRequest buildPageRequest(int page, int size) {

  22. Sort sort = new Sort(Direction.DESC,"createTime");

  23. return new PageRequest(page,size, sort);

  24. }

  25. /**

  26. * 建立查询条件

  27. * @author liuxg

  28. * @date 2016年3月30日 下午2:04:39

  29. */

  30. private class MySpec implements Specification<Task>{

  31. @Override

  32. public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

  33. //1.混合条件查询

  34. /*Path<String> exp1 = root.get("taskName");

  35. Path<Date> exp2 = root.get("createTime");

  36. Path<String> exp3 = root.get("taskDetail");

  37. Predicate predicate = cb.and(cb.like(exp1, "%taskName%"),cb.lessThan(exp2, new Date()));

  38. return cb.or(predicate,cb.equal(exp3, "kkk"));

  39. 类似的sql语句为:

  40. Hibernate:

  41. select

  42. count(task0_.id) as col_0_0_

  43. from

  44. tb_task task0_

  45. where

  46. (

  47. task0_.task_name like ?

  48. )

  49. and task0_.create_time<?

  50. or task0_.task_detail=?

  51. */

  52. //2.多表查询

  53. /*Join<Task,Project> join = root.join("project", JoinType.INNER);

  54. Path<String> exp4 = join.get("projectName");

  55. return cb.like(exp4, "%projectName%");

  56. Hibernate:

  57. select

  58. count(task0_.id) as col_0_0_

  59. from

  60. tb_task task0_

  61. inner join

  62. tb_project project1_

  63. on task0_.project_id=project1_.id

  64. where

  65. project1_.project_name like ?*/

  66. return null ;

  67. }

  68. }

  69. }

3.实体类task代码如下

  1. @Entity

  2. @Table(name = "tb_task")

  3. public class Task {

  4. private Long id ;

  5. private String taskName ;

  6. private Date createTime ;

  7. private Project project;

  8. private String taskDetail ;

  9. @Id

  10. @GeneratedValue(strategy = GenerationType.IDENTITY)

  11. public Long getId() {

  12. return id;

  13. }

  14. public void setId(Long id) {

  15. this.id = id;

  16. }

  17. @Column(name = "task_name")

  18. public String getTaskName() {

  19. return taskName;

  20. }

  21. public void setTaskName(String taskName) {

  22. this.taskName = taskName;

  23. }

  24. @Column(name = "create_time")

  25. @DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss")

  26. public Date getCreateTime() {

  27. return createTime;

  28. }

  29. public void setCreateTime(Date createTime) {

  30. this.createTime = createTime;

  31. }

  32. @Column(name = "task_detail")

  33. public String getTaskDetail() {

  34. return taskDetail;

  35. }

  36. public void setTaskDetail(String taskDetail) {

  37. this.taskDetail = taskDetail;

  38. }

  39. @ManyToOne(fetch = FetchType.LAZY)

  40. @JoinColumn(name = "project_id")

  41. public Project getProject() {

  42. return project;

  43. }

  44. public void setProject(Project project) {

  45. this.project = project;

  46. }

  47. }

通过重写toPredicate方法,返回一个查询 Predicate,spring data jpa会帮我们进行查询。

也许你觉得,每次都要写一个类来实现Specification很麻烦,那或许你可以这么写

  1. public class TaskSpec {

  2. public static Specification<Task> method1(){

  3. return new Specification<Task>(){

  4. @Override

  5. public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

  6. return null;

  7. }

  8. };

  9. }

  10. public static Specification<Task> method2(){

  11. return new Specification<Task>(){

  12. @Override

  13. public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

  14. return null;

  15. }

  16. };

  17. }

  18. }

那么用的时候,我们就这么用

Page<Task> tasks = this.taskDao.findAll(TaskSpec.method1(), pageReq);

JpaSpecificationExecutor的介绍就到这里,下次再看看怎么通过写hql或者sql语句来进行查询

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这