Spring?Data?Exists查詢最佳方法編寫示例

簡介
在這篇文章中,我將向你展示編寫Spring Data Exists查詢的最佳方法,從SQL的角度來看,它是高效的。
在做咨詢的時候,我遇到了幾個常用的選項,而開發(fā)者卻不知道其實還有更好的選擇。
領(lǐng)域模型
讓我們假設(shè)我們有以下Post 實體。

slug 屬性是一個業(yè)務(wù)鍵,意味著它有一個唯一的約束,為此,我們可以用下面的注解來注解它 @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提供了一種從方法名派生查詢的方法,所以你可以寫一個findBy 查詢來模擬存在,就像這樣。
@Repository
public interface PostRepository
extends JpaRepository<Post, Long> {
Optional<Post> findBySlug(String slug);
}
由于findBySlug 方法是用來獲取Post 實體的,我見過這樣的情況:這個方法被用來進行平等檢查,就像下面的例子。
assertTrue(
postRepository.findBySlug(slug).isPresent()
);
這種方法的問題在于,實體的獲取實際上只是為了檢查是否有一個與所提供的過濾條件相關(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 查詢來獲取實體以檢查其存在性是一種資源浪費,因為如果你在slug 屬性上有一個索引的話,你不僅不能使用覆蓋查詢,而且你必須通過網(wǎng)絡(luò)將實體結(jié)果集發(fā)送到JDBC驅(qū)動程序,只是默默地將其丟棄。
使用實例查詢來檢查存在性
另一個非常流行的,但效率低下的檢查存在性的方法是使用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 規(guī)范給出的屬性時,該實體將被用作參考。
當執(zhí)行上述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 Exists查詢。
用existsBy查詢方法檢查存在性
Spring Data提供了一個existsBy 查詢方法,我們可以在PostRepository ,定義如下。
@Repository
public interface PostRepository
extends JpaRepository<Post, Long> {
boolean existsBySlug(String slug);
}
當在PostgreSQL或MySQL上調(diào)用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執(zhí)行計劃看起來如下。
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 操作并不影響性能,因為它反正是在一個記錄的結(jié)果集上完成。
用COUNT SQL查詢來檢查存在性
模擬存在性的另一個選擇是使用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列值。
然而,一般來說,對于返回有多條記錄的結(jié)果集的查詢,你應(yīng)該傾向于使用EXISTS ,而不是COUNT ,正如Lukas Eder在這篇文章中所解釋的那樣。
在PostgreSQL和MySQL上調(diào)用existsBySlugWithCount 方法時。
assertTrue(
postRepository.existsBySlugWithCount(slug)
);
Spring Data會執(zhí)行以下SQL查詢。
SELECT
count(p.id) > 0 AS col_0_0_
FROM
post p
WHERE
p.slug = 'high-performance-java-persistence'
而且,這個查詢的PostgreSQL執(zhí)行計劃看起來如下。
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步驟,但由于只有一條記錄需要計算,所以這個步驟非??臁?/p>
用CASE WHEN EXISTS SQL查詢來檢查存在性
最后一個模擬存在的選項是使用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)
);
這個查詢的PostgreSQL執(zhí)行計劃看起來如下。
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檢查一條記錄的存在,最簡單的方法是使用existsBy 查詢方法。
而且,如果查詢比較復(fù)雜,你不能用Spring Data的查詢方法來表達,你可以使用COUNT或CASE WHEN EXISTS查詢,因為它們同樣快速。
以上就是Spring Data Exists查詢最佳方法編寫示例的詳細內(nèi)容,更多關(guān)于Spring Data Exists查詢的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java連接MySQL數(shù)據(jù)庫并實現(xiàn)數(shù)據(jù)交互功能
在現(xiàn)代應(yīng)用中,數(shù)據(jù)庫是不可或缺的一部分,Java 作為一種廣泛使用的編程語言,提供了豐富的 API 來與各種數(shù)據(jù)庫進行交互,本文將詳細介紹如何在 Java 中連接 MySQL 數(shù)據(jù)庫,并實現(xiàn)基本的數(shù)據(jù)交互功能,需要的朋友可以參考下2024-10-10
Java中Object.wait()和LockSupport.park()的用法
Object.wait()和LockSupport.park()都是用來使當前線程等待的方法,本文主要介紹了Java中Object.wait()和LockSupport.park()的用法,具有一定的參考價值,感興趣的可以了解一下2024-05-05

