您好,登錄后才能下訂單哦!
小編給大家分享一下Spring Boot JPA Repository之existsBy查詢方法失效怎么辦,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
引言: Spring Boot號稱微服務的利器,在結合了Spring Data與JPA之后,更是如虎添翼,開發快速的不像話,本文將講述一個關于JPA中一個詭異問題的診斷分析過程以及修復方法。
JDK 1.8 Spring 4.2 Spring Boot 1.5.9
在Spring Data中的Repository接口中創建了一個檢查數據是否存在的接口方法:
@Repository public interface VideoEntityRepository extends JpaRepository<VideoEntity, Long> { ........ public boolean existsByUserIdAndName(long userId, String name); }
VideoEntity的類如下:
@SuppressWarnings("serial") @Table(name="flook_video") @Entity @Data @EqualsAndHashCode(callSuper=true) public class VideoEntity extends BaseEntity { @Column(name="user_id") private long userId; @Column(name="name") private String name; @Column(name="change_version") private double changeVersion; }
在調用方法existsBy方法的時候,返回的結果一直為false, 結果不正確,在偶的期望中,其執行結果應該不會出錯的?那問題出在哪里呢?
Spring Data提供了若干非常實用的擴展,將數據庫表日常的CRUD操作都進行很好的實現,并提供了若干擴展機制,基于一套簡單易用的命名規則,來基于聲明式實現場景的數據庫查詢操作:
countByColumName existsByColumnName
上述兩種方式都是由Spring Data來幫助動態生成SQL的。
除了基于countBy/existsBy兩種方式之外,可以直接使用@Query方式來標注特定的SQL或者JPQL來實現功能,由于這種方式需要些SQL,功能上完全覆蓋,但是工作量略大,不是最優的方式。
由于底層使用了Hibernate/JPA/Spring Data來實現的數據訪問層的實現,所以,最好的方式當然是查看動態生成的SQL了,于是找到了生成的SQL語句:
select videoentit0_.id as col_0_0_ from video videoentit0_ where videoentit0_.user_id=? and videoentit0_.name=? limit ?
表的名稱是video,這個名稱是不對的。這個情況是怎么發生的呢?
經過分析發現,是由于在代碼中存在兩個類名完全相同VideoEntity的類,雖然在Repository中我們的的確確沒有引用錯誤相關的,問題應該出在當Spring Data碰到兩個相同的類名之時,其實不知道如何來生成SQL的,換句話說,其應該是基于類名而非類路徑來動態生成執行SQL的。
TODO: 基于源代碼中找到相關部分內容。
將當前的VideoEntity重新命名,確保不存在重名的問題,即使類的路徑不一樣,但是類名一樣,也是會產生這樣的問題。
從一個側面來分析,在Spring Data中所有的DataBean都是需要使用全路徑的類名的,否則同樣會出現問題。
意思是如果在接口中定義的查詢方法符合它的命名規則,就可以不用寫實現,目前支持的關鍵字如下。
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 |
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.特殊的參數: 還可以直接在方法的參數上加入分頁或排序的參數,比如:
Page<UserModel>findByName(String name, Pageable pageable); List<UserModel>findByName(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<UserModel> findByAge(int age);
3:然后就可以使用了,Spring會先找是否有同名的NamedQuery,如果有,那么就不會按照接口定義的方法來解析。
e.還可以使用@Query來指定本地查詢,只要設置nativeQuery為true,比如:
@Query(value="select* from tbl_user where name like %?1" ,nativeQuery=true) publicList<UserModel> findByUuidOrAge(String name);
注意:當前版本的本地查詢不支持翻頁和動態的排序
f.使用命名化參數,使用@Param即可,比如:
@Query(value="selecto from UserModel o where o.name like %:nn") publicList<UserModel> 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 Boot JPA Repository之existsBy查詢方法失效怎么辦”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。