Spring?Data?JPA查詢方式及方法名查詢規(guī)則介紹
Spring Data JPA查詢方式及方法名查詢規(guī)則
Spring Data JPA
通過(guò)解析方法名創(chuàng)建查詢
在執(zhí)行查詢時(shí),Spring Data JPA框架會(huì)把方法名進(jìn)行解析,解析到前綴比如 get、getBy、find、findBy、read、readBy時(shí),會(huì)先把這些前綴截取掉,然后對(duì)剩下部分進(jìn)行解析,剩下部分分為兩種:一是只有屬性名,二是屬性名+條件;條件很好解析,解析的關(guān)鍵在于屬性名,下面拿一個(gè)具體的例子來(lái)幫助大家更好的理解屬性名解析規(guī)則。
解析規(guī)則例子:比如實(shí)體為Product,方法為findByGoodsTypeDetail ();
1、首先截取掉 findBy,然后對(duì)剩下的屬性進(jìn)行解析;
2、先判斷 goodsTypeDetail(根據(jù) POJO 規(guī)范,首字母變?yōu)樾?,下同)是否?Product的一個(gè)屬性,如果是,則表示根據(jù)該屬性進(jìn)行查詢;如果沒(méi)有該屬性,繼續(xù)第三步;
3、從右往左截取第一個(gè)大寫字母開(kāi)頭的字符串(本方法為 Detail),然后對(duì)比剩下的字符串(本方法為goodsType)是否為 Product的一個(gè)屬性,如果是,則表示根據(jù)該屬性進(jìn)行查詢;如果沒(méi)有該屬性,則重復(fù)第三步,繼續(xù)從右往左截?。ù颂帪門ypeDetail,剩下goods),就這樣一直循環(huán)到最終;假設(shè) goods為 Product的一個(gè)屬性,則說(shuō)明goods不是常量類型,而是一個(gè)對(duì)象類型;
4、此時(shí)剩下字符串 TypeDetail,先判斷goods對(duì)象中是否有 typeDetail屬性,如果有,則表示該方法最終是根據(jù) "Product.goods.typeDetail" 的值進(jìn)行查詢;如果沒(méi)有該屬性,則繼續(xù)按照第三步的規(guī)則從右往左截取,最終表示根據(jù) "Product.goods.type.detail" 的值進(jìn)行查詢。
不過(guò)這種解析規(guī)則不是完美的,也存在bug,不注意可能會(huì)掉到這個(gè)坑里,比如Product中有一個(gè)屬性叫g(shù)oods,同時(shí)還有一個(gè)屬性叫g(shù)oodsType,這時(shí)在解析時(shí)會(huì)出現(xiàn)混亂,不過(guò)可以在屬性之間加上 "_"來(lái)解決這個(gè)問(wèn)題,注意:"_"是加在查詢方法上的,不是加在屬性名上的;比如 "findByGoods_TypeDetail()" (當(dāng)Product中不存在goods_TypeDetail時(shí),是給解析器說(shuō)明Goods為一個(gè)對(duì)象)或"findByGoodsType_Detail()"(當(dāng)Product中不存在goodsType_Detail時(shí),是給解析器說(shuō)明GoodsType為一個(gè)對(duì)象)。
查詢時(shí),很多時(shí)候需要同時(shí)使用多個(gè)屬性進(jìn)行查詢,而且查詢的條件也各不相同,Spring Data JPA 為此提供了一些條件查詢的關(guān)鍵字,我把常用的都整理了一下,如下表:
|
關(guān)鍵字 |
對(duì)應(yīng)SQL關(guān)鍵字 |
示例 |
|
列名 |
根據(jù)列名查詢 |
findByName(String name);自動(dòng)解析findBy后面的列名,然后根據(jù)列名查詢。 |
|
In |
等價(jià)于SQL 中的 in |
findByNameIn(Collection<String> nameList) ;參數(shù)可以是集合、數(shù)組、不定長(zhǎng)參數(shù); |
|
Like |
等價(jià)于SQL 中的 like |
findByNameLike(String name); |
| NotLike | 等價(jià)于SQL 中的 not like | findByNameNotLike(String name); |
|
And |
等價(jià)于SQL 中的 and |
findByNameAndPwd(String name, String pwd); |
|
Or |
等價(jià)于SQL 中的 or |
findByIdOrCode(String id, String code); |
|
Between |
等價(jià)于SQL 中的 between |
findByNumBetween(int max, int min); |
|
OrderBy |
等價(jià)于SQL 中的 order by |
findByNameOrderByNumAsc(String name); |
|
IsNull |
等價(jià)于SQL 中的 is null |
findByNameIsNull(); |
| IsNotNull | 等價(jià)于SQL 中的 is not null | findByNameIsNotNull(); |
| NotNull | 等價(jià)于SQL 中的 is not null | findByNameNotNull();--和IsNotNull 一樣,建議使用IsNotNull |
| Not | 等價(jià)于SQL 中的 ! = | findByNameNot(String name); |
| NotIn | 等價(jià)于SQL 中的 not in | findByNameNotIn(Collection<String> nameList) ;參數(shù)可以是集合、數(shù)組、不定長(zhǎng)參數(shù); |
| LessThan | 等價(jià)于SQL 中的 < | findByNumLessThan(int num); |
|
GreaterThan |
等價(jià)于SQL 中的 > |
findByNumGreaterThan(int num); |
使用 @Query 創(chuàng)建查詢
1、使用 @Query 提供的位置編號(hào)查詢:格式為":位置編號(hào)",然后方法中的參數(shù)按 JPQL 查詢語(yǔ)句的位置編號(hào)順序書寫。 如下:
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 命名參數(shù)查詢:格式為": 變量",同時(shí)在方法的參數(shù)前面使用 @Param 將方法參數(shù)與JPQL中的命名參數(shù)對(duì)應(yīng)。如下:
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 將查詢操作標(biāo)識(shí)為更新操作:在使用 @Query 的同時(shí)使用 @Modifying ,這樣會(huì)生成一個(gè)更新的操作,而非查詢。如下:
@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 默認(rèn)帶的查詢方法

@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 構(gòu)造函數(shù)從略
}
簡(jiǎn)單的擴(kuò)展-以字段為關(guān)鍵字進(jìn)行查詢
list<RoleData> findByXXX(xxx) 其中 XXX 對(duì)應(yīng)數(shù)據(jù)庫(kù)中的字段,例如:
@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語(yǔ)句:
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=?
當(dāng)然 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 進(jìn)行復(fù)雜查詢
例如:
@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 進(jìn)行復(fù)雜查詢
先來(lái)看一下 JpaSpecificationExecutor 接口

以 findAll(Specification<T>) 為例進(jìn)行說(shuō)明:
Specification<T> 可以理解為一個(gè)查詢條件。findAll 以這個(gè)條件為基準(zhǔn)進(jìn)行查詢,也就是我們?cè)趕ql 里寫的 whre xxx 轉(zhuǎn)為 Specification 來(lái)寫。
首先要讓我們的 repository 繼承 JpaSpecificationExecutor
@Repository
public interface RoleRepository extends CrudRepository<RoleData, Integer>, JpaSpecificationExecutor<RoleData> {
接下來(lái),將這個(gè)查詢 [ select * from role where role like '%a%' ] 轉(zhuǎn)為一個(gè)簡(jiǎn)單的 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;
}
};
然后直接按如下方式調(diào)用即可:
roleRepository.findAll(spec);
Specification 里又衍生出了好幾個(gè)類,分別介紹一下:
Predicate
因?yàn)槲覀儗?shí)現(xiàn) Specification 接口時(shí),只需要實(shí)現(xiàn) Predicate toPredicate() 方法。而 Specification 上文中我們當(dāng)做搜索條件來(lái)理解了,那么也可以簡(jiǎn)單的把 Predicate 視為搜索條件。
CriteriaBuilder
用于構(gòu)建搜索條件 Predicater 的。
回想一下SQL搜索條件怎么寫
where attribute = xx where attribute > xx where attribute < xx where attribute like %xx%
注意這里有三要素:
- attribute 搜索指定的數(shù)據(jù)庫(kù)字段
- 操作符 大于 小于 等于
- 具體數(shù)據(jù)
CriteriaBuilder提供了一系列靜態(tài)方法構(gòu)建這三要素。
比如
- CriteriaBuilder.like(數(shù)據(jù)庫(kù)字段, 具體數(shù)據(jù))
- CriteriaBuilder.equal(數(shù)據(jù)庫(kù)字段, 具體數(shù)據(jù))
其中 數(shù)據(jù)庫(kù)字段 不能直接寫字符串,需要下一個(gè)工具類 Root 的 get 方法獲取。
Root
root.get( String attributeName ) 參數(shù) attributeName 就是數(shù)據(jù)庫(kù)里的字段名
現(xiàn)在相信讀者可以理解 我們剛才寫的 那個(gè)完整的 Specification了。
再下來(lái)再上一個(gè)稍微復(fù)雜點(diǎn)的例子:
[ 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;
}
};
其實(shí)也很簡(jiǎn)單,就是多了 criteriaBuilder.or criteriaBuilder.and 來(lái)把多個(gè) Predicate 合成一個(gè)新的 Predicate
最后一個(gè)例子:
可以通過(guò)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;
}
};
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于MyBatis中映射對(duì)象關(guān)系的舉例
這篇文章主要介紹了關(guān)于MyBatis中映射對(duì)象關(guān)系的舉例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
java代碼實(shí)現(xiàn)C盤文件統(tǒng)計(jì)工具
今天周末,給大家分享基于java代碼實(shí)現(xiàn)C盤文件統(tǒng)計(jì)工具,在這小編使用的版本是Maven-3.9.9,jdk1.8,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-07-07
Java中的Gradle與Groovy的區(qū)別及存在的關(guān)系
這篇文章主要介紹了Java中的Gradle與Groovy的區(qū)別及存在的關(guān)系,Groovy是一種JVM語(yǔ)言,它可以編譯為與Java相同的字節(jié)碼,并且可以與Java類無(wú)縫地互操作,Gradle是Java項(xiàng)目中主要的構(gòu)建系統(tǒng)之一,下文關(guān)于兩者的詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-02-02
Java基礎(chǔ)教程之類型轉(zhuǎn)換與多態(tài)
這篇文章主要介紹了Java基礎(chǔ)教程之類型轉(zhuǎn)換與多態(tài),本文講解了 基本類型轉(zhuǎn)換、 upcast與多態(tài)、 Object類等內(nèi)容,需要的朋友可以參考下2014-09-09
初學(xué)者易上手的SSH-struts2 01環(huán)境搭建(圖文教程)
下面小編就為大家?guī)?lái)一篇初學(xué)者易上手的SSH-struts2 01環(huán)境搭建(圖文教程)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
spring boot jpa寫原生sql報(bào)Cannot resolve table錯(cuò)誤解決方法
在本篇文章里小編給大家整理的是關(guān)于spring boot jpa寫原生sql報(bào)Cannot resolve table錯(cuò)誤的解決方法,需要的朋友學(xué)習(xí)下。2019-11-11
java編程下字符串的16位,32位md5加密實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇java編程下字符串的16位,32位md5加密實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09
JavaWeb ServletContext基礎(chǔ)與應(yīng)用詳細(xì)講解
ServletConfig對(duì)象,叫Servlet配置對(duì)象。主要用于加載配置文件的初始化參數(shù)。我們知道一個(gè)Web應(yīng)用里面可以有多個(gè)servlet,如果現(xiàn)在有一份數(shù)據(jù)需要傳給所有的servlet使用,那么我們就可以使用ServletContext對(duì)象了2023-01-01

