Spring Data JPA查询方式及方法名查询规则介绍
作者:七星程序员 发布时间:2022-07-13 19:31:20
Spring Data JPA查询方式及方法名查询规则
Spring Data JPA
通过解析方法名创建查询
在执行查询时,Spring Data JPA框架会把方法名进行解析,解析到前缀比如 get、getBy、find、findBy、read、readBy时,会先把这些前缀截取掉,然后对剩下部分进行解析,剩下部分分为两种:一是只有属性名,二是属性名+条件;条件很好解析,解析的关键在于属性名,下面拿一个具体的例子来帮助大家更好的理解属性名解析规则。
解析规则例子:比如实体为Product,方法为findByGoodsTypeDetail ();
1、首先截取掉 findBy,然后对剩下的属性进行解析;
2、先判断 goodsTypeDetail(根据 POJO 规范,首字母变为小写,下同)是否为 Product的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第三步;
3、从右往左截取第一个大写字母开头的字符串(本方法为 Detail),然后对比剩下的字符串(本方法为goodsType)是否为 Product的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第三步,继续从右往左截取(此处为TypeDetail,剩下goods),就这样一直循环到最终;假设 goods为 Product的一个属性,则说明goods不是常量类型,而是一个对象类型;
4、此时剩下字符串 TypeDetail,先判断goods对象中是否有 typeDetail属性,如果有,则表示该方法最终是根据 "Product.goods.typeDetail" 的值进行查询;如果没有该属性,则继续按照第三步的规则从右往左截取,最终表示根据 "Product.goods.type.detail" 的值进行查询。
不过这种解析规则不是完美的,也存在bug,不注意可能会掉到这个坑里,比如Product中有一个属性叫goods,同时还有一个属性叫goodsType,这时在解析时会出现混乱,不过可以在属性之间加上 "_"来解决这个问题,注意:"_"是加在查询方法上的,不是加在属性名上的;比如 "findByGoods_TypeDetail()" (当Product中不存在goods_TypeDetail时,是给解析器说明Goods为一个对象)或"findByGoodsType_Detail()"(当Product中不存在goodsType_Detail时,是给解析器说明GoodsType为一个对象)。
查询时,很多时候需要同时使用多个属性进行查询,而且查询的条件也各不相同,Spring Data JPA 为此提供了一些条件查询的关键字,我把常用的都整理了一下,如下表:
关键字 | 对应SQL关键字 | 示例 |
列名 | 根据列名查询 | findByName(String name);自动解析findBy后面的列名,然后根据列名查询。 |
In | 等价于SQL 中的 in | findByNameIn(Collection<String> nameList) ;参数可以是集合、数组、不定长参数; |
Like | 等价于SQL 中的 like | findByNameLike(String name); |
NotLike | 等价于SQL 中的 not like | findByNameNotLike(String name); |
And | 等价于SQL 中的 and | findByNameAndPwd(String name, String pwd); |
Or | 等价于SQL 中的 or | findByIdOrCode(String id, String code); |
Between | 等价于SQL 中的 between | findByNumBetween(int max, int min); |
OrderBy | 等价于SQL 中的 order by | findByNameOrderByNumAsc(String name); |
IsNull | 等价于SQL 中的 is null | findByNameIsNull(); |
IsNotNull | 等价于SQL 中的 is not null | findByNameIsNotNull(); |
NotNull | 等价于SQL 中的 is not null | findByNameNotNull();--和IsNotNull 一样,建议使用IsNotNull |
Not | 等价于SQL 中的 ! = | findByNameNot(String name); |
NotIn | 等价于SQL 中的 not in | findByNameNotIn(Collection<String> nameList) ;参数可以是集合、数组、不定长参数; |
LessThan | 等价于SQL 中的 < | findByNumLessThan(int num); |
GreaterThan | 等价于SQL 中的 > | findByNumGreaterThan(int num); |
使用 @Query 创建查询
1、使用 @Query 提供的位置编号查询:格式为":位置编号",然后方法中的参数按 JPQL 查询语句的位置编号顺序书写。 如下:
public interface ProductDao extends Repository<Product , Long> {
@Query("select * from Product p where p.id= ?1")
public Product findById(Long id);
@Query("select * from Product p where p.type = ?1 and p.name =?2")
public Page<Product> findByTypeAndName(
Integer type,String name,Pageable pageable);
}
2、使用@Query 命名参数查询:格式为": 变量",同时在方法的参数前面使用 @Param 将方法参数与JPQL中的命名参数对应。如下:
public interface ProductDao extends Repository<Product , Long> {
@Query("from Product p where p.goodsName= :name")
public Product findByGoodsName(@Param("name")String name);
@Query("from Product p where p.num < :num")
public Page<Product> findByNumLessThan(
@Param("num")Integer num,Pageable pageable);
}
3、 使用 @Modifying 将查询操作标识为更新操作:在使用 @Query 的同时使用 @Modifying ,这样会生成一个更新的操作,而非查询。如下:
@Query("update Product p set p.name = ?1 where p.id = ?2")
@Modifying
public int updateName(String name, int id);
JPA 常用查询方法记录
以这张表为例:
+-------------+--------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| role | varchar(45) | NO | | NULL | |
| permissions | varchar(512) | NO | | NULL | |
| create_time | datetime | NO | | CURRENT_TIMESTAMP | |
| status | varchar(45) | NO | | NULL | |
| role_name | varchar(45) | NO | | NULL | |
+-------------+--------------+------+-----+-------------------+----------------+
CrudRepository 默认带的查询方法
@Repository
public interface RoleRepository extends CrudRepository<RoleData, Integer> {
}
@Entity
@Table(name = "role", catalog = "message_push")
public class RoleData implements java.io.Serializable {
@Id
@GeneratedValue
private Integer id;
private String role;
private String permissions;
private Long create_time;
private Integer status;
// getter setter 构造函数从略
}
简单的扩展-以字段为关键字进行查询
list<RoleData> findByXXX(xxx) 其中 XXX 对应数据库中的字段,例如:
@Repository
public interface RoleRepository extends CrudRepository<RoleData, Integer> {
List<RoleData> findByRole(String role);
List<RoleData> findByStatus(String status);
}
还可以多字段AND 查询:
@Repository
public interface RoleRepository extends CrudRepository<RoleData, Integer> {
List<RoleData> findByRoleAndStatus(String role, String status);
}
在 application.properties 中加入以下配置 spring.jpa.show-sql=true 可以看到SQL语句:
Hibernate: select roledata0_.id as id1_0_, roledata0_.create_time as create_t2_0_, roledata0_.permissions as permissi3_0_, roledata0_.role as role4_0_, roledata0_.status as status5_0_ from message_push.role roledata0_ where roledata0_.role=? and roledata0_.status=?
当然 or 也是可以:
List<RoleData> findByRoleOrStatus(String role, String status);
Hibernate: select roledata0_.id as id1_0_, roledata0_.create_time as create_t2_0_, roledata0_.permissions as permissi3_0_, roledata0_.role as role4_0_, roledata0_.status as status5_0_ from message_push.role roledata0_ where roledata0_.role=? or roledata0_.status=?
使用@Query 进行复杂查询
例如:
@Query(value = "select * from role where role = ?1", nativeQuery = true)
List<RoleData> searchByRole(String role);
或 sql in 用法
@Query(value = "select * from role where role in (?1) and status = 'valid'", nativeQuery = true)
List<RoleData> searchByRoleList(List<String> targetList);
又或 sql like 用法:
@Query(value = "select * from role where role like %?1%", nativeQuery = true)
List<RoleData> searchByRole(String keyWord);
使用 Specification 进行复杂查询
先来看一下 JpaSpecificationExecutor 接口
以 findAll(Specification<T>) 为例进行说明:
Specification<T> 可以理解为一个查询条件。findAll 以这个条件为基准进行查询,也就是我们在sql 里写的 whre xxx 转为 Specification 来写。
首先要让我们的 repository 继承 JpaSpecificationExecutor
@Repository
public interface RoleRepository extends CrudRepository<RoleData, Integer>, JpaSpecificationExecutor<RoleData> {
接下来,将这个查询 [ select * from role where role like '%a%' ] 转为一个简单的 Specification。
final Specification<RoleData> spec = new Specification<RoleData> () {
@Override
public Predicate toPredicate(Root<RoleData> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Predicate predicate = criteriaBuilder.like(root.get("role"), "%a%");
return predicate;
}
};
然后直接按如下方式调用即可:
roleRepository.findAll(spec);
Specification 里又衍生出了好几个类,分别介绍一下:
Predicate
因为我们实现 Specification 接口时,只需要实现 Predicate toPredicate() 方法。而 Specification 上文中我们当做搜索条件来理解了,那么也可以简单的把 Predicate 视为搜索条件。
CriteriaBuilder
用于构建搜索条件 Predicater 的。
回想一下SQL搜索条件怎么写
where attribute = xx
where attribute > xx
where attribute < xx
where attribute like %xx%
注意这里有三要素:
attribute 搜索指定的数据库字段
操作符 大于 小于 等于
具体数据
CriteriaBuilder提供了一系列静态方法构建这三要素。
比如
CriteriaBuilder.like(数据库字段, 具体数据)
CriteriaBuilder.equal(数据库字段, 具体数据)
其中 数据库字段 不能直接写字符串,需要下一个工具类 Root 的 get 方法获取。
Root
root.get( String attributeName ) 参数 attributeName 就是数据库里的字段名
现在相信读者可以理解 我们刚才写的 那个完整的 Specification了。
再下来再上一个稍微复杂点的例子:
[ select * from role where role like '%a%' and (id > 11 or id < 8) ]
final Specification<RoleData> spec = new Specification<RoleData> () {
@Override
public Predicate toPredicate(Root<RoleData> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Predicate roleLikeaPredicate = criteriaBuilder.like(root.get("role"), "%a%");
Predicate idLessThan8Predicate = criteriaBuilder.lessThan(root.get("id"), 8);
Predicate idGreaterThan12Predicate = criteriaBuilder.greaterThan(root.get("id"), 11);
Predicate idCombindedPredicate = criteriaBuilder.or(idLessThan8Predicate, idGreaterThan12Predicate);
Predicate predicate = criteriaBuilder.and(idCombindedPredicate, roleLikeaPredicate);
return predicate;
}
};
其实也很简单,就是多了 criteriaBuilder.or criteriaBuilder.and 来把多个 Predicate 合成一个新的 Predicate
最后一个例子:
可以通过root.get(xx).in(List<> list) 也是可以直接返回 Predicate 的
final Specification<RoleData> spec2 = new Specification<RoleData> () {
@Override
public Predicate toPredicate(Root<RoleData> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<String> alist = new ArrayList<String>();
alist.add("admin");
Predicate predicate = root.get("role").in(alist);
return predicate;
}
};
来源:https://blog.csdn.net/m0_37679452/article/details/83278000


猜你喜欢
- 1. 效果图展示2. 工程目录结构注意: webapp下的resources目录放置easyui和js(jQuery文件是另外的) 
- 目录前言线程基础1、创建线程2、暂停线程3、线程等待4、线程终止C#中的lock关键字总结前言最近由于工作的需要,一直在使用C#的多线程进行
- 需要用到的知识:注解、AOP、ExpiringMap(带有有效期的映射)我们可以自定义注解,把注解添加到我们的接口上。定义一个切面,执行方法
- 本文实例为大家分享了java实现KFC点餐系统的具体代码,供大家参考,具体内容如下package KFC点餐系统;//food 类 publ
- package com;import java.util.Arrays; public class sjf { &nbs
- 1.ACSII码加密//ACSII码加密 private static string
- 随着C语言的学习慢慢结束,博主也要开始学习一门新语言了,那就是java。所以博主将会开启一个新的关于java的专栏,所以想要慢慢和我一起学习
- 本文实例为大家分享了java中文传值乱码问题,以及解决方法,供大家参考,具体内容如下一般编码格式设置:1.可以经过两次编码处理,即设置字符集
- 本文实例分析了采用C#实现软件自动更新的方法,是非常实用的功能,值得学习和借鉴。具体如下:1.问题概述:长期以来,广大程序员为到底是使用Cl
- 本文实例讲述了Android编程实现系统重启与关机的方法。分享给大家供大家参考,具体如下:最近在做个东西,巧合碰到了sharedUserId
- 本文实例为大家分享了Java实现多线程在线聊天的具体代码,供大家参考,具体内容如下上一篇博客通过UDP实现了聊天,但只能单方面发送消息,这次
- 本文实例讲述了Android编程开发之Spinner组件用法。分享给大家供大家参考,具体如下:Spinner组件组要用显示一个下拉列表,在使
- 前言记一次为了节省代码没有在方法体中声明HttpServletRequest,而用autowire直接注入所钻的坑结论:给心急的人。 直接在
- Android插件开启对新Api的支持这一天小王导入了一个库,上线之后直接崩了一大片? 找到其中的问题:什么鬼哦?安卓8.0一下无法使用?
- Hadoop是什么?Hadoop是一个开发和运行处理大规模数据的软件平台,是Appach的一个用java语言实现开源软件框架,实现在大量计算
- 经常要检测某些IP地址范围段的计算机是否在线。有很多的方法,比如进入到网关的交换机上去查询、使用现成的工具或者编写一个简单的DOS脚本等等,
- 主流有几种方法,一种是用下面的函数publicstaticStringgetProperty(Stringkey)键相关值的描述java.v
- 前面我们已经学习了平时实际开发中用得较多的线性布局(LinearLayout)与相对布局(RelativeLayout), 其实学完这两个基
- 本文实例讲述了C#实现程序开机启动的方法。分享给大家供大家参考,具体如下://此方法把启动项加载到注册表中//获得应用程序路径string
- 以下是SpringBoot项目中的常用配置类、jar包坐标等通用配置pom文件<!-- --><!-- 自定义配置文件