欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Data JPA查询方式及方法名查询规则介绍

程序员文章站 2022-03-10 09:01:54
目录spring data jpa查询方式及方法名查询规则一、通过解析方法名创建查询二、使用 @query 创建查询jpa 常用查询方法记录crudrepository 默认带的查询方法简单的扩展-以...

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 默认带的查询方法

Spring Data JPA查询方式及方法名查询规则介绍

@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 接口

Spring Data JPA查询方式及方法名查询规则介绍

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。