溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring?Data?Exists查詢方法如何編寫

發(fā)布時間:2022-08-02 09:32:50 來源:億速云 閱讀:131 作者:iii 欄目:開發(fā)技術

本篇內容介紹了“Spring Data Exists查詢方法如何編寫”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    Spring?Data?Exists查詢方法如何編寫

    簡介

    在這篇文章中,我將向你展示編寫Spring Data Exists查詢的最佳方法,從SQL的角度來看,它是高效的。

    在做咨詢的時候,我遇到了幾個常用的選項,而開發(fā)者卻不知道其實還有更好的選擇。

    領域模型

    讓我們假設我們有以下Post 實體。

    Spring?Data?Exists查詢方法如何編寫

    slug 屬性是一個業(yè)務鍵,意味著它有一個唯一的約束,為此,我們可以用下面的注解來注解它 @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()
    );

    這種方法的問題在于,實體的獲取實際上只是為了檢查是否有一個與所提供的過濾條件相關的記錄。

    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)絡將實體結果集發(fā)送到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 規(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上調用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 操作并不影響性能,因為它反正是在一個記錄的結果集上完成。

    用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列值。

    然而,一般來說,對于返回有多條記錄的結果集的查詢,你應該傾向于使用EXISTS ,而不是COUNT ,正如Lukas Eder在這篇文章中所解釋的那樣。

    在PostgreSQL和MySQL上調用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步驟,但由于只有一條記錄需要計算,所以這個步驟非常快。

    用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);
    }

    而且,我們可以像這樣調用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)

    所以,這和之前的LIMITCOUNT 查詢一樣快。在其他數(shù)據(jù)庫上,你可能要檢查一下,看看是否有什么不同。

    “Spring Data Exists查詢方法如何編寫”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

    AI