Spring Data Exists查询最佳方法编写示例

目录
  • 简介
  • 领域模型
  • 如何不使用Spring Data来写Exists查询?
    • 用findBy查询模拟存在
    • 使用实例查询来检查存在性
  • 如何使用Spring Data编写Exists查询
    • 用existsBy查询方法检查存在性
    • 用COUNT SQL查询来检查存在性
    • 用CASE WHEN EXISTS SQL查询来检查存在性
  • 结论

简介

在这篇文章中,我将向你展示编写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来写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 属性上有一个索引的话,你不仅不能使用覆盖查询,而且你必须通过网络将实体结果集发送到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 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执行计划看起来如下。

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会执行以下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查询来检查存在性

最后一个模拟存在的选项是使用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)

所以,这和之前的LIMITCOUNT 查询一样快。在其他数据库上,你可能要检查一下,看看是否有什么不同。

结论

因此,如果你想用Spring Data检查一条记录的存在,最简单的方法是使用existsBy 查询方法。

而且,如果查询比较复杂,你不能用Spring Data的查询方法来表达,你可以使用COUNT或CASE WHEN EXISTS查询,因为它们同样快速。

以上就是Spring Data Exists查询最佳方法编写示例的详细内容,更多关于Spring Data Exists查询的资料请关注我们其它相关文章!

(0)

相关推荐

  • 关于@Query注解的用法(Spring Data JPA)

    目录 @Query注解的用法 1.一个使用@Query注解的简单例子 2.Like表达式 3.使用NativeSQLQuery 4.使用@Param注解注入参数 5.SPEL表达式(使用时请参考最后的补充说明) 6.一个较完整的例子 7.解释例6中错误的原因 @Query注解使用详情 常用属性 取值方式 CRUD @Query注解的用法 1.一个使用@Query注解的简单例子 @Query(value = "select name,author,price from Book b where b

  • spring.datasource.schema配置详解

    目录 1.现将sql文件放在resources下的sql文件夹下 2.新建数据库mybatis 3.配置yml 用springboot2.0执行sql脚本: 1.现将sql文件放在resources下的sql文件夹下 2.新建数据库mybatis 3.配置yml spring: datasource: # 数据源基本配置 username: root password: 123 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql

  • Spring Data JPA 关键字Exists的用法说明

    Spring Data JPA 关键字Exists 查询数据库中的此数据是否已存在: 例子: 查询sys_user表中的一个user是否存在,类SysUser对应的是数据库中的sys_user表,SysUserId是表sys_user的主键类(ID类). 如果查询一个user,user的accountNo为demo. userID为demo1,表sys_user的主键是accountNo和userID,下面代码中的方法是查询这个user是否存在,如果存在则返回true,不存在则返回false.

  • SpringBoot Test 多线程报错的根本原因(dataSource already closed)

    背景 使用Springboot test进行相关测试的时候,发现开启线程操作数据库的时候异常. 排查方法 将线程移除,采用并行的方式,操作数据库正常. 根本原因 SpringBoot Test 主线程退出,导致Spring 容器关闭. Spring容器关闭,导致DruidDataSource 关闭 此时用户线程去访问已关闭的数据源,导致报错. 解决方法 提供一个全局的线程池,然后使用线程池开启线程操作,然后添加监听器,监听线程池里面是否有未完成的任务,如果有则不关闭容器. @Component

  • springboot连接redis并动态切换database的实现方法

    众所周知,redis多有个db,在jedis中可以使用select方法去动态的选择redis的database,但在springboot提供的StringRedisTemplate中确,没有该方法,好在StringRedisTemplate预留了一个setConnectionFactory方法,本文主为通过修改ConnectionFactory从而达到动态切换database的效果. springboot连接redis pom.xml文件中引入spring-boot-starter-redis,

  • Spring Data Exists查询最佳方法编写示例

    目录 简介 领域模型 如何不使用Spring Data来写Exists查询? 用findBy查询模拟存在 使用实例查询来检查存在性 如何使用Spring Data编写Exists查询 用existsBy查询方法检查存在性 用COUNT SQL查询来检查存在性 用CASE WHEN EXISTS SQL查询来检查存在性 结论 简介 在这篇文章中,我将向你展示编写Spring Data Exists查询的最佳方法,从SQL的角度来看,它是高效的. 在做咨询的时候,我遇到了几个常用的选项,而开发者却不

  • Spring Data Jpa框架最佳实践示例

    目录 前言 扩展接口用法 SPRINGDATAJPA最佳实践 一.继承SIMPLEJPAREPOSITORY实现类 二.集成QUERYDSL结构化查询 1.快速集成 2.丰富BaseJpaRepository基类 3.最终的BaseJpaRepository形态 三.集成P6SPY打印执行的SQL 结语 前言 Spring Data Jpa框架的目标是显著减少实现各种持久性存储的数据访问层所需的样板代码量. Spring Data Jpa存储库抽象中的中央接口是Repository.它需要领域实

  • Spring Data JPA查询方式及方法名查询规则介绍

    目录 Spring Data JPA查询方式及方法名查询规则 一.通过解析方法名创建查询 二.使用 @Query 创建查询 JPA 常用查询方法记录 CrudRepository 默认带的查询方法 简单的扩展-以字段为关键字进行查询 使用@Query 进行复杂查询 使用 Specification 进行复杂查询 Predicate CriteriaBuilder Root Spring Data JPA查询方式及方法名查询规则 Spring Data JPA 一.通过解析方法名创建查询 在执行查

  • 基于Spring Data Jest的Elasticsearch数据统计示例

    命令查询职责分离模式(Command Query Responsibility Segregation,CQRS)从业务上分离修改 (Command,增,删,改,会对系统状态进行修改)和查询(Query,查,不会对系统状态进行修改)的行为.从而使得逻辑更加清晰,便于对不同部分进行针对性的优化. CQRS有以下几点有点: 1.分工明确,可以负责不同的部分: 2.将业务上的命令和查询的职责分离能够提高系统的性能.可扩展性和安全性.并且在系统的演化中能够保持高度的灵活性,能够防止出现CRUD模式中,对

  • 使用Spring Data Jpa查询全部并排序

    目录 Spring Data Jpa查询全部并排序 1.Repository层只需要简单地extends JpaRepository 2.Service层中如下 JPA之排序问题 1.总览 2.使用JPA / JQL API排序 3.使用JPA条件查询对象API进行排序 Spring Data Jpa查询全部并排序 1.Repository层只需要简单地extends JpaRepository 依旧不用写任何东西.因为它自己其实有一个findAll(Sort sort)的方法,直接用就可以了.

  • spring data jpa查询一个实体类的部分属性方式

    目录 springdatajpa查询一个实体类的部分属性 首先我们定义两个实体类 然后创建person实体类的repository 返回结果只包含firstName和lastName两个属性 springdatajpa查询部分字段.多余附加字段 第一种方法:使用model查询时转化 第二种方法:在service里边转换成JSON 第三种方法:select语句部分字段使用默认值 spring data jpa查询一个实体类的部分属性 使用Spring Data Repository查询时候,通常情

  • SpringBoot整合Spring Data JPA的详细方法

    目录 前言 核心概念 新建SpringBoot项目 创建MySQL数据库 创建实体类 创建Repository 创建处理器 准备SQL文件 编写配置文件 最终效果 启动SpringBoot项目 查看数据库 自动更新数据表结构 测试JPA的增删改查 测试查询所有 测试保存数据 测试更新数据 测试删除数据 前言 Spring Data JPA 是更大的 Spring Data 家族的一部分,可以轻松实现基于 JPA 的存储库.该模块处理对基于 JPA 的数据访问层的增强支持.它使构建使用数据访问技术

  • 基于Spring Data的AuditorAware审计功能的示例代码

    Spring Data提供支持审计功能:即由谁在什么时候创建或修改实体.Spring Data提供了在实体类的属性上增加@CreatedBy,@LastModifiedBy,@CreatedDate,@LastModifiedDate注解,并配置相应的配置项,即可实现审计功能,有系统自动记录 createdBy CreatedDate lastModifiedBy lastModifiedDate 四个属性的值,下面为具体的配置项. 示例 创建一个实体类 package com.hfcsbc.i

  • 使用Spring Data JDBC实现DDD聚合的示例代码

    本文讨论了Spring Data JDBC如何实现DDD中聚合根存储的设计思路,其中主要讨论了是不是每个实体都需要一个对应数据表,这种问题需要根据具体情况而定. Spring Data JDBC比JPA更容易理解,比如对象引用特性会很有趣.作为第一个示例,请考虑以下领域模型: class PurchaseOrder { private @Id Long id; private String shippingAddress; private Set<OrderItem> items = new

  • SpringBoot+Spring Data JPA整合H2数据库的示例代码

    目录 前言 Maven依赖 Conroller 实体类 Repository 数据库脚本文件 配置文件 启动项目 访问H2数据库 查看全部数据 H2数据库文件 运行方式 前言 H2数据库是一个开源的关系型数据库.H2采用java语言编写,不受平台的限制,同时支持网络版和嵌入式版本,有比较好的兼容性,支持相当标准的sql标准 提供JDBC.ODBC访问接口,提供了非常友好的基于web的数据库管理界面 官网:http://www.h2database.com/ Maven依赖 <!--jpa-->

随机推荐