Spring?Data?Exists查詢最佳方法編寫示例
簡(jiǎn)介
在這篇文章中,我將向你展示編寫Spring Data Exists查詢的最佳方法,從SQL的角度來看,它是高效的。
在做咨詢的時(shí)候,我遇到了幾個(gè)常用的選項(xiàng),而開發(fā)者卻不知道其實(shí)還有更好的選擇。
領(lǐng)域模型
讓我們假設(shè)我們有以下Post
實(shí)體。
slug
屬性是一個(gè)業(yè)務(wù)鍵,意味著它有一個(gè)唯一的約束,為此,我們可以用下面的注解來注解它 @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來寫Exists查詢?
首先,讓我們從各種方法開始,這些方法雖然很流行,但你最好避免使用。
用findBy查詢模擬存在
Spring Data提供了一種從方法名派生查詢的方法,所以你可以寫一個(gè)findBy
查詢來模擬存在,就像這樣。
@Repository public interface PostRepository extends JpaRepository<Post, Long> { Optional<Post> findBySlug(String slug); }
由于findBySlug
方法是用來獲取Post
實(shí)體的,我見過這樣的情況:這個(gè)方法被用來進(jìn)行平等檢查,就像下面的例子。
assertTrue( postRepository.findBySlug(slug).isPresent() );
這種方法的問題在于,實(shí)體的獲取實(shí)際上只是為了檢查是否有一個(gè)與所提供的過濾條件相關(guān)的記錄。
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
查詢來獲取實(shí)體以檢查其存在性是一種資源浪費(fèi),因?yàn)槿绻阍?code>slug 屬性上有一個(gè)索引的話,你不僅不能使用覆蓋查詢,而且你必須通過網(wǎng)絡(luò)將實(shí)體結(jié)果集發(fā)送到JDBC驅(qū)動(dòng)程序,只是默默地將其丟棄。
使用實(shí)例查詢來檢查存在性
另一個(gè)非常流行的,但效率低下的檢查存在性的方法是使用Query By Example功能。
assertTrue( postRepository.exists( Example.of( new Post().setSlug(slug), ExampleMatcher.matching() .withIgnorePaths(Post_.ID) .withMatcher(Post_.SLUG, exact()) ) ) );
Query By Example功能建立了一個(gè)Post
實(shí)體,在匹配所提供的ExampleMatcher
規(guī)范給出的屬性時(shí),該實(shí)體將被用作參考。
當(dāng)執(zhí)行上述Query By Example方法時(shí),Spring Data會(huì)生成與之前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功能對(duì)于獲取實(shí)體可能很有用,但是將其與Spring Data JPA的exists
通用方法Repository
,效率并不高。
如何使用Spring Data編寫Exists查詢
有更好的方法來編寫Spring Data Exists查詢。
用existsBy查詢方法檢查存在性
Spring Data提供了一個(gè)existsBy
查詢方法,我們可以在PostRepository
,定義如下。
@Repository public interface PostRepository extends JpaRepository<Post, Long> { boolean existsBySlug(String slug); }
當(dāng)在PostgreSQL或MySQL上調(diào)用existsBySlug
方法時(shí)。
assertTrue( postRepository.existsBySlug(slug) );
Spring Data會(huì)生成以下SQL查詢。
SELECT p.id AS col_0_0_ FROM post p WHERE p.slug = 'high-performance-java-persistence' LIMIT 1
這個(gè)查詢的PostgreSQL執(zhí)行計(jì)劃看起來如下。
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)
所以,這個(gè)查詢非常快,而且額外的LIMIT
操作并不影響性能,因?yàn)樗凑窃谝粋€(gè)記錄的結(jié)果集上完成。
用COUNT SQL查詢來檢查存在性
模擬存在性的另一個(gè)選擇是使用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
查詢?cè)谶@種特殊情況下可以正常工作,因?yàn)槲覀冋谄ヅ湟粋€(gè)UNIQUE列值。
然而,一般來說,對(duì)于返回有多條記錄的結(jié)果集的查詢,你應(yīng)該傾向于使用EXISTS
,而不是COUNT
,正如Lukas Eder在這篇文章中所解釋的那樣。
在PostgreSQL和MySQL上調(diào)用existsBySlugWithCount
方法時(shí)。
assertTrue( postRepository.existsBySlugWithCount(slug) );
Spring Data會(huì)執(zhí)行以下SQL查詢。
SELECT count(p.id) > 0 AS col_0_0_ FROM post p WHERE p.slug = 'high-performance-java-persistence'
而且,這個(gè)查詢的PostgreSQL執(zhí)行計(jì)劃看起來如下。
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操作有一個(gè)額外的Aggregate步驟,但由于只有一條記錄需要計(jì)算,所以這個(gè)步驟非??臁?/p>
用CASE WHEN EXISTS SQL查詢來檢查存在性
最后一個(gè)模擬存在的選項(xiàng)是使用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); }
而且,我們可以像這樣調(diào)用existsBySlugWithCase
方法。
assertTrue( postRepository.existsBySlugWithCase(slug) );
這個(gè)查詢的PostgreSQL執(zhí)行計(jì)劃看起來如下。
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
查詢一樣快。在其他數(shù)據(jù)庫上,你可能要檢查一下,看看是否有什么不同。
結(jié)論
因此,如果你想用Spring Data檢查一條記錄的存在,最簡(jiǎn)單的方法是使用existsBy
查詢方法。
而且,如果查詢比較復(fù)雜,你不能用Spring Data的查詢方法來表達(dá),你可以使用COUNT或CASE WHEN EXISTS查詢,因?yàn)樗鼈兺瑯涌焖佟?/p>
以上就是Spring Data Exists查詢最佳方法編寫示例的詳細(xì)內(nèi)容,更多關(guān)于Spring Data Exists查詢的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java使用wait()和notify()線程間通訊的實(shí)現(xiàn)
Java 線程通信是將多個(gè)獨(dú)立的線程個(gè)體進(jìn)行關(guān)聯(lián)處理,使得線程與線程之間能進(jìn)行相互通信,本文就介紹了java使用wait()和notify()線程間通訊的實(shí)現(xiàn),感興趣的可以了解一下2023-09-09Springboot事件監(jiān)聽與@Async注解詳解
這篇文章主要介紹了Springboot事件監(jiān)聽與@Async注解詳解,在開發(fā)中經(jīng)??梢岳肧pring事件監(jiān)聽來實(shí)現(xiàn)觀察者模式,進(jìn)行一些非事務(wù)性的操作,如記錄日志之類的,需要的朋友可以參考下2024-01-01使用Springboot整合GridFS實(shí)現(xiàn)文件操作
這篇文章主要介紹了使用Springboot整合GridFS實(shí)現(xiàn)文件操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java中for、foreach、stream區(qū)別和性能比較詳解
for、foreach、stream都可以循環(huán)處理數(shù)據(jù),如果單純當(dāng)循環(huán)使用,for、foreach、stream哪個(gè)性能更好,這篇文章主要給大家介紹了關(guān)于Java中for、foreach、stream區(qū)別和性能的相關(guān)資料,需要的朋友可以參考下2024-03-03