您好,登錄后才能下訂單哦!
本篇內容介紹了“Spring Data Exists查詢方法如何編寫”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在這篇文章中,我將向你展示編寫Spring Data Exists查詢的最佳方法,從SQL的角度來看,它是高效的。
在做咨詢的時候,我遇到了幾個常用的選項,而開發者卻不知道其實還有更好的選擇。
讓我們假設我們有以下Post
實體。
slug
屬性是一個業務鍵,意味著它有一個唯一的約束,為此,我們可以用下面的注解來注解它 @NaturalIdHibernate注解。
@Entity @Entity @Table( name = "post", uniqueConstraints = @UniqueConstraint( name = "UK_POST_SLUG", columnNames = "slug" ) ) public class Post { @Id private Long id; private String title; @NaturalId private String slug; public Long getId() { return id; } public Post setId(Long id) { this.id = id; return this; } public String getTitle() { return title; } public Post setTitle(String title) { this.title = title; return this; } public Post setSlug(String slug) { this.slug = slug; return this; } }
首先,讓我們從各種方法開始,這些方法雖然很流行,但你最好避免使用。
Spring Data提供了一種從方法名派生查詢的方法,所以你可以寫一個findBy
查詢來模擬存在,就像這樣。
@Repository public interface PostRepository extends JpaRepository<Post, Long> { Optional<Post> findBySlug(String slug); }
由于findBySlug
方法是用來獲取Post
實體的,我見過這樣的情況:這個方法被用來進行平等檢查,就像下面的例子。
assertTrue( postRepository.findBySlug(slug).isPresent() );
這種方法的問題在于,實體的獲取實際上只是為了檢查是否有一個與所提供的過濾條件相關的記錄。
SELECT p.id AS id1_0_, p.slug AS slug2_0_, p.title AS title3_0_ FROM post p WHERE p.slug = 'high-performance-java-persistence'
使用fidnBy
查詢來獲取實體以檢查其存在性是一種資源浪費,因為如果你在slug
屬性上有一個索引的話,你不僅不能使用覆蓋查詢,而且你必須通過網絡將實體結果集發送到JDBC驅動程序,只是默默地將其丟棄。
另一個非常流行的,但效率低下的檢查存在性的方法是使用Query By Example功能。
assertTrue( postRepository.exists( Example.of( new Post().setSlug(slug), ExampleMatcher.matching() .withIgnorePaths(Post_.ID) .withMatcher(Post_.SLUG, exact()) ) ) );
Query By Example功能建立了一個Post
實體,在匹配所提供的ExampleMatcher
規范給出的屬性時,該實體將被用作參考。
當執行上述Query By Example方法時,Spring Data會生成與之前findBy
方法所生成的相同的SQL查詢。
SELECT p.id AS id1_0_, p.slug AS slug2_0_, p.title AS title3_0_ FROM post p WHERE p.slug = 'high-performance-java-persistence'
雖然Query By Example功能對于獲取實體可能很有用,但是將其與Spring Data JPA的exists
通用方法Repository
,效率并不高。
有更好的方法來編寫Spring Data Exists查詢。
Spring Data提供了一個existsBy
查詢方法,我們可以在PostRepository
,定義如下。
@Repository public interface PostRepository extends JpaRepository<Post, Long> { boolean existsBySlug(String slug); }
當在PostgreSQL或MySQL上調用existsBySlug
方法時。
assertTrue( postRepository.existsBySlug(slug) );
Spring Data會生成以下SQL查詢。
SELECT p.id AS col_0_0_ FROM post p WHERE p.slug = 'high-performance-java-persistence' LIMIT 1
這個查詢的PostgreSQL執行計劃看起來如下。
Limit (cost=0.28..8.29 rows=1 width=8) (actual time=0.021..0.021 rows=1 loops=1) -> Index Scan using uk_post_slug on post p (cost=0.28..8.29 rows=1 width=8) (actual time=0.020..0.020 rows=1 loops=1) Index Cond: ((slug)::text = 'high-performance-java-persistence'::text) Planning Time: 0.088 ms Execution Time: 0.033 ms
還有,MySQL的,像這樣。
-> Limit: 1 row(s) (cost=0.00 rows=1) (actual time=0.001..0.001 rows=1 loops=1) -> Rows fetched before execution (cost=0.00 rows=1) (actual time=0.000..0.000 rows=1 loops=1)
所以,這個查詢非常快,而且額外的LIMIT
操作并不影響性能,因為它反正是在一個記錄的結果集上完成。
模擬存在性的另一個選擇是使用COUNT查詢。
@Repository public interface PostRepository extends JpaRepository<Post, Long> { @Query(value = """ select count(p.id) = 1 from Post p where p.slug = :slug """ ) boolean existsBySlugWithCount(@Param("slug") String slug); }
COUNT
查詢在這種特殊情況下可以正常工作,因為我們正在匹配一個UNIQUE列值。
然而,一般來說,對于返回有多條記錄的結果集的查詢,你應該傾向于使用EXISTS
,而不是COUNT
,正如Lukas Eder在這篇文章中所解釋的那樣。
在PostgreSQL和MySQL上調用existsBySlugWithCount
方法時。
assertTrue( postRepository.existsBySlugWithCount(slug) );
Spring Data會執行以下SQL查詢。
SELECT count(p.id) > 0 AS col_0_0_ FROM post p WHERE p.slug = 'high-performance-java-persistence'
而且,這個查詢的PostgreSQL執行計劃看起來如下。
Aggregate (cost=8.29..8.31 rows=1 width=1) (actual time=0.023..0.024 rows=1 loops=1) -> Index Scan using uk_post_slug on post p (cost=0.28..8.29 rows=1 width=8) (actual time=0.019..0.020 rows=1 loops=1) Index Cond: ((slug)::text = 'high-performance-java-persistence'::text) Planning Time: 0.091 ms Execution Time: 0.044 ms
而在MySQL上。
-> Aggregate: count('1') (actual time=0.002..0.002 rows=1 loops=1) -> Rows fetched before execution (cost=0.00 rows=1) (actual time=0.000..0.000 rows=1 loops=1)
盡管COUNT操作有一個額外的Aggregate步驟,但由于只有一條記錄需要計算,所以這個步驟非常快。
最后一個模擬存在的選項是使用CASE WHEN EXISTS本地SQL查詢。
@Repository public interface PostRepository extends JpaRepository<Post, Long> { @Query(value = """ SELECT CASE WHEN EXISTS ( SELECT 1 FROM post WHERE slug = :slug ) THEN 'true' ELSE 'false' END """, nativeQuery = true ) boolean existsBySlugWithCase(@Param("slug") String slug); }
而且,我們可以像這樣調用existsBySlugWithCase
方法。
assertTrue( postRepository.existsBySlugWithCase(slug) );
這個查詢的PostgreSQL執行計劃看起來如下。
Result (cost=8.29..8.29 rows=1 width=1) (actual time=0.021..0.022 rows=1 loops=1) InitPlan 1 (returns $0) -> Index Only Scan using uk_post_slug on post (cost=0.27..8.29 rows=1 width=0) (actual time=0.020..0.020 rows=1 loops=1) Index Cond: (slug = 'high-performance-java-persistence'::text) Heap Fetches: 1 Planning Time: 0.097 ms Execution Time: 0.037 ms
而在MySQL上。
-> Rows fetched before execution (cost=0.00 rows=1) (actual time=0.000..0.000 rows=1 loops=1) -> Select #2 (subquery in projection; run only once) -> Limit: 1 row(s) (cost=0.00 rows=1) (actual time=0.000..0.001 rows=1 loops=1) -> Rows fetched before execution (cost=0.00 rows=1) (actual time=0.000..0.000 rows=1 loops=1)
所以,這和之前的LIMIT
和COUNT
查詢一樣快。在其他數據庫上,你可能要檢查一下,看看是否有什么不同。
“Spring Data Exists查詢方法如何編寫”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。