【译】什么时候使用 Spring 6 JdbcClient

原文地址:Spring 6 JdbcClient: When and How to Use it?

一、前言

自 Spring 6.1 起,JdbcClient 为 JDBC 查询和更新操作提供了统一的客户端 API,从而提供了更流畅、更简化的交互模型。本教程演示了如何在各种场景中使用 JdbcClient。

二、Spring 中的数据库访问方法

Spring 框架提供了几种不同的数据库访问方法。两种流行的方法是

  • 直接执行 SQL 语句的统一 API,例如 JdbcTemplate
  • ORM 框架支持,例如 Hibernate、JPA

统一 API 提供了一种直接高效的方法,允许开发人员以更直接的方式处理 SQL 查询。这种方法的关键组件包括:JdbcTemplate、NamedParameterJdbcTemplate 和 JdbcClient。

对象关系映射(ORM)框架(如 Hibernate)为关系数据库提供了一个抽象层,允许开发人员使用面向对象范例与数据库交互。这使开发人员能够将 Java 对象映射到数据库表,封装并隔离 SQL 查询的细节。

在直接 SQL 执行和 ORM 框架支持之间做出选择取决于多种因素,包括应用程序的复杂性、开发人员的偏好和具体的项目要求。直接 SQL 执行因其简单性和可控性而受到青睐,而 ORM 框架则在优先考虑面向对象设计和抽象的情况下表现出色。

三、JdbcClient 与 JdbcTemplate 的区别

JdbcTemplate 是 Spring Data 中的一个核心类,可简化 JDBC 的使用并消除与传统 JDBC 使用相关的大量模板代码。它提供了执行 SQL 查询、更新和存储过程的方法。

它具有以下功能:

  • 执行 SQL 查询、更新和存储过程
  • 使用? 占位符传递查询参数
  • 通过 RowMapper 或 ResultSetExtractor 进行查询结果行映射

以下面的代码为例

public int getCountOfUsers(String name) {String sql = "SELECT COUNT(*) FROM users WHERE name = ?";return jdbcTemplate.queryForObject(sql, Integer.class, name);
}```JdbcClientSpring 6.1 中引入的增强型统一 JDBC 客户端 API,为命名和位置参数语句提供了流畅的交互模型。它旨在进一步简化 JDBC 操作。它具有以下功能:- 统一支持命名参数和位置参数- 旨在进一步简化 JDBC 操作- 作为不断发展的 Spring 框架的一部分引入以下面的代码为例```java
public int getCountOfUsers(String name) {return jdbcClient.sql("SELECT COUNT(*) FROM users WHERE name = ?").param(name).query(Integer.class).single();
}

在 JdbcTemplate 和 JdbcClient 之间做出选择取决于项目的上下文、Spring 版本和开发人员的偏好。这两种工具都能简化数据库交互,各有各的优势和用例。

四、开始使用 JdbcClient

4.1. Maven

要在 Spring 应用程序中使用 JdbcClient,我们必须确保项目使用的是 Spring 6.1 或 Spring Boot 3.2 的最低版本。

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0-M2</version><relativePath/> <!-- lookup parent from repository -->
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- other dependencies -->
</dependencies>

4.2. 配置和初始化

要配置 JdbcClient,请确保 Repository 类中包含了 DataSource Bean。

一旦配置完成,JdbcClient 类的实例是线程安全的。一种常用的方法是将 DataSource Bean 依赖注入到 Repository/Dao 类中。JdbcClient 是在存储库构造函数中使用语句 JdbcClient.create() 创建的。Spring 框架会使用构造器注入自动注入 DataSource Bean。

@Repository
public class PersonRepository {private final JdbcClient jdbcClient;public PersonRepository(DataSource dataSource) {this.jdbcClient = JdbcClient.create(dataSource);}//...
}

4.3. 使用 JdbClient 的简单示例

一旦构建了 JdbcClient 实例,我们就可以使用它的便捷方法来执行 SQL 查询。

public Optional<Person> findById(Long id) {String sql = "select id, first_name, last_name, created_at from person where id = :id";return jdbcClient.sql(sql).param("id", id).query((rs, rowNum) -> new Person(rs.getInt("id"), rs.getString("first_name"), rs.getString("last_name"), rs.getTimestamp("created_at").toInstant())).optional();
}

五、为 SQL 语句传递参数

JdbcClient API 在接受 SQL 参数方面非常灵活。让我们来看看几种方法。

5.1. 位置参数

位置参数是查询语句中的占位符,通过其在语句中的位置顺序来识别。这些参数在 SQL 语句中用占位符?

在下面的示例中,查询参数 first_name、last_name 和 created_at 是按分配给方法 StatementSpec.param() 的顺序隐式注册的。

String sql = "insert into person(first_name, last_name, created_at) values (?, ?, ?)";jdbcClient.sql(sql).param("Alex").param("Dave").param(Timestamp.from(Instant.now())).update();

我们还可以使用 StatementSpec.params() 方法将参数作为 var-args 传入,如下所示:

jdbcClient.sql(sql).params("Alex", "Dave", Timestamp.from(Instant.now())).update(keyHolder);

此外,还可以将参数传递为 List。

jdbcClient.sql(sql).params(List.of("Alex", "Dave", Timestamp.from(Instant.now()))).update(keyHolder);

如果我们想进一步确保按正确顺序绑定参数,我们甚至可以传递参数索引,以确保万无一失。

jdbcClient.sql(sql).param(1, "Alex").param(2, "Dave").param(3, Timestamp.from(Instant.now())).update()

5.2. 命名参数

与 NamedParameterJdbcTemplate 类似,JdbcClient 也支持使用占位符 “:paramName” 格式命名 SQL 语句参数。

String sql = "insert into person(first_name, last_name, created_at) values (:firstName, :lastName, :createdAt)";jdbcClient.sql(sql).param("firstName, "Alex").param("lastName", "Dave").param("createdAt", Timestamp.from(Instant.now())).update();

也可以以 Map 的形式传递命名参数,其中键代表命名参数,值在运行时传递给查询。

Map<String, ?> paramMap = Map.of("firstName", "Alex","lastName", "Dave","createdAt", Timestamp.from(Instant.now())
);jdbcClient.sql(sql).params(paramMap).update();

5.3. 对象实例参数(Parameter Source)

为了让事情变得更简单,也可以传递一个字段名与命名参数相匹配的对象(记录类、带有 bean 属性的类或普通字段持有者)。

在下面的示例中,我们使用 Person 对象中的值对数据库执行 INSERT 操作。

Person person = new Person(null, "Clark", "Kent", Instant.now());jdbcClient.sql(sql).paramSource(person).update();

Person 类是一种记录类型,它的字段名与命名参数相匹配。

public record Person(Long id, String firstName, String lastName, Instant createdAt) {
}

同样,我们也可以使用 SimplePropertySqlParameterSourceBeanPropertySqlParameterSource 策略。

SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(person);jdbcClient.sql(sql).paramSource(namedParameters).update();

六、将结果集映射到对象

6.1. 使用自定义 RowMapper

从数据库中查询行时,我们可以使用结果集检索列的值,如第 3.3 节所示。但如果我们想增加灵活性和代码的简洁性,可以考虑使用 RowMapper。

下面的 PersonRowMapper 类实现了 RowMapper 接口,并覆盖了 mapRow() 方法,该方法包含将数据库行映射到 Person 实例的逻辑。

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import com.howtodoinjava.model.Person;
import org.springframework.jdbc.core.RowMapper;public class PersonRowMapper implements RowMapper<Person> {private PersonRowMapper() {}private static final PersonRowMapper INSTANCE = new PersonRowMapper();public static PersonRowMapper getInstance() {return INSTANCE;}@Overridepublic Person mapRow(ResultSet rs, int rowNum) throws SQLException {return new Person(rs.getLong("id"),rs.getString("first_name"),rs.getString("last_name"),getInstantFromTimestamp(rs.getTimestamp("created_at")));}private Instant getInstantFromTimestamp(Timestamp timestamp) {return (timestamp != null) ? timestamp.toInstant() : null;}
}

现在,我们可以在 query() 方法中使用 PersonRowMapper,Spring 会在内部使用映射器,并直接获取 Person 实例。

String querySql = "select id, first_name, last_name, created_at from person where id = :id";Optional<Person> personOptional = jdbcClient.sql(querySql).param("id", 1).query(PersonRowMapper.getInstance()).optional();

6.2. 使用类 Mapping

如果因为在类字段和数据库列之间有一个直接的字段映射,而创建 PersonRowMapper 显得很费力,那么可以直接传递 query() 方法的类类型,这样也可以。

String querySql = "select id, first_name, last_name, created_at from person where id = :id";Optional<Person> personOptional = jdbcClient.sql(querySql).param("id", 1).query(Person.class).optional();

译者注:再看一下 Person 的记录类是如何定义的,看起来会自动完成下划线分隔命名和驼峰命名的转换。

public record Person(Long id, String firstName, String lastName, Instant createdAt) {
}

七、SQL 查询和更新操作

JdbcClient 支持所有类型的数据库操作,如选择、创建、更新和删除记录。

  • query() 方法执行给定的 SQL查询,返回的查询结果提供了多个封装方法,如映射类(Class Mapping)、行映射器(RowMapper)、行回调处理程序(RowCallbackHandler)和结果集提取器(ResultSetExtractor)
  • update() 方法将提供的 SQL 语句作为更新执行。它总是返回一个受影响行数的 int 值。

这些方法的示例在前面的章节中已经介绍过,不再赘述。

八、批量插入和存储过程的考虑因素

JdbcClient 是一个灵活但非常简化的界面,仅用于 JDBC 查询/更新语句。如果您需要做更复杂的事情,如执行批处理操作或调用存储过程,JdbcClient 可能无法提供您需要的所有功能。

在这种情况下,您可能需要使用 Spring 提供的其他工具,如 SimpleJdbcInsert 或 SimpleJdbcCall。

另外,您也可以直接使用更基本的 JdbcTemplate 来完成 JdbcClient 无法完全覆盖的任务。这就好比在工具箱中装有不同的工具,您可以根据工作需要选择最合适的工具。

九、结论

在本教程中,我们探讨了使用 Spring 6 JdbcClient 进行查询和更新操作的初始配置和基础知识。如前所述,JdbcClient 为 JDBC 交互提供了简化和统一的 API,使代码更加简洁易读。

学习愉快

Github 上的源码地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/161716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【VScode】安装配置、插件及远程SSH连接

一、VSCode安装 二、配置安装插件 三、配置远程连接SSH 四、MinGW 一、VSCode安装 VS官网 Visual Studio Code - Code Editing. Redefined下载安装包&#xff1a; 二、配置安装插件 安装中文插件 配置字体为20 配置文件–>首选项->设置->Font Size为20 设置 VSC…

【libGDX】使用Mesh绘制圆形

1 前言 使用Mesh绘制三角形 中介绍了绘制三角形的方法&#xff0c;使用Mesh绘制矩形 中介绍了绘制矩形的方法&#xff0c;本文将介绍绘制圆形的方法。 libGDX 以点、线段、三角形为图元&#xff0c;没有提供绘制圆形的接口。要绘制圆形边框&#xff0c;必须通过割圆法逼近圆形&…

问题:vue2使用watch监视对象属性,但是这个监视只执行了第一次,后面就没反应了

错误版本 这个版本node.a的监视只会执行一次 data(){node:{}, }, watch:{"node.a":{handler(newVal,oldVal){console.log("node.a改变了")}}, }正确版本 这个可以正常监视node.a data(){node:{a:,}, }, watch:{"node.a":{handler(newVal,old…

macos创建xcframework及签名

前言 Framework 可以理解为封装了共享资源的具有层次结构的文件夹&#xff0c;共享资源可以是 nib文件、国际化字符串文件、头文件、库文件等等。它同时也是一个 Bundle&#xff0c;里面的内容可以通过 Bundle 相关 API 来访问。Framework 可以是 static framework 或 dynamic…

CSS中实现元素居中的七种方法

在前端开发中&#xff0c;经常需要将元素居中显示&#xff0c;CSS提供了多种技术方法来实现元素的居中&#xff0c;在不同场景下有不同的使用方法、不同的效果&#xff0c;需要特别记住它们的应用场景才能够正常的居中。这篇文章就大致总结一下CSS中的居中方法。 一、元素分类…

[递归]有理数树

有理数树 题目描述 二叉树指的是一种树形结构&#xff0c;它的每个结点有至多两个子节点。 现在有一个由有理数组成的无穷二叉树形状如下&#xff1a; 1/1______|______| |1/2 2/1___|___ ___|___| | | | 1/3 3/2 2/3 3/1在p/q结点位…

3.计算机网络补充

2.5 HTTPS 数字签名&#xff1a;发送端将消息使⽤ hash 函数⽣成摘要&#xff0c;并使⽤私钥加密后得到“数字签名”&#xff0c;并将其附在消息之后。接收端使⽤公钥对“数字签名”解密&#xff0c;确认发送端身份&#xff0c;之后对消息使⽤ hash 函数处理并与接收到的摘要对…

【中间件】服务化中间件理论intro

中间件middleware 内容管理 intro服务化middleware架构注册中心intro服务治理系统intro 本文主要intro服务化中间件的探讨 去年cfeng写了一篇博客走马观花般阐述了Spring Cloud下面的各种中间件&#xff0c;连深入使用都谈不上&#xff0c;只能说intro&#xff0c;在实际work中…

数字孪生助力污水处理升级

随着科技的发展&#xff0c;数字孪生技术在各行各业中得到了广泛应用。在污水处理领域&#xff0c;数字孪生技术为流程监控、效率提升、问题诊断等提供了强有力的支持。本文就借用山海鲸可视化软件的污水处理解决方案为大家介绍数字孪生在污水处理领域的作用。 一、实时监控 …

VsCode学习

一、在VsCode 上编写第一个C语言 在VsCode上写代码都是先打开文件夹&#xff0c;这样也方便管理代码和编译器产生的可执行程序&#xff0c;VsCode生成的配置文件等。 1.1打开文件夹 写代码前&#xff0c;首先创立一个文件夹存储以后我们写的VsCode代码&#xff0c;便于管理。…

SpringBoot:kaptcha生成验证码

GitHub项目地址&#xff1a;GitHub - penggle/kaptcha: kaptcha - A kaptcha generation engine. kaptcha介绍 kaptcha官网&#xff08;Google Code Archive - Long-term storage for Google Code Project Hosting.&#xff09;对其介绍如下&#xff0c; kaptcha十分易于安装…

linux CentOS7.6安装jenkins(小白版本)

前言 本人是一个前端开发者&#xff0c;由于有时候需要发版自己的东西&#xff0c;所以想搞一个Jenkins玩玩&#xff0c;看了网上好多教程&#xff0c;但是都不是针对小白的&#xff0c;比如linux怎么输入&#xff0c;怎么结束&#xff0c;自己也是搞了好久踩了好多坑 所以记录…

Linux(Kali\Ubuntu\CentOS\arm-Linux)安装Powershell

文章目录 Linux(Kali\Ubuntu\CentOS\arm-Linux)安装Powershell启动PowershellKaliUbuntuCentOSarm-Linux离线安装参考链接 Linux(Kali\Ubuntu\CentOS\arm-Linux)安装Powershell 启动Powershell pwshKali apt update && apt -y install powershellUbuntu # 更新包列…

filebeat配置解析【待续】

目录 filebeat概览filebeat是如何工作的工作原理采集日志注册表发送日志 容器日志采集的三种方式方式一&#xff1a;Filebeat 与 应用运行在同一容器&#xff08;不推荐&#xff09;方式二&#xff1a;Filebeat 与 应用运行不在同一容器方式三&#xff1a;通过 Kubernetes File…

oracle “ORA-25153:临时表空间为空”

从生产上面备份出来了一个数据库&#xff0c;应用在使用时显示ORA-25153临时表空间为空的报错&#xff0c;原因一般是数据库迁移时&#xff0c;没有迁移完整造成的 解决方法 1.创建新的临时表空间temp2 create temporary tablespace temp2 tempfile DATA size 100M autoexten…

【Python百题】--1.输出Hello,world【2023.11.22】

1.问题描述 编写一个Python程序&#xff0c;将字符串 ‘Hello World!’ 存储在变量 str 中&#xff0c;然后使用 print 语句将其打印出来。 2.解决思路 使用print()函数即可 3.代码实现 print("Hello,world")4.运行结果

系统移植-交叉编译工具链

不同架构的机器码 与 汇编语言 都不可移植&#xff0c; 且二者一一对应 c语言中三种成分&#xff1a; 1.分号结尾的叫做语句 语句可以让CPU执行&#xff0c;可以进行预处理&#xff0c;编译等生成机器码 2.#开头的为预处理指令 不带分号 CPU无法执行 3.注释&#xff0c;…

vue中列表渲染

列表渲染 实际开发中&#xff0c;使用每条数据的唯一标识作为key,也就是对于数组列表&#xff0c;对象中的属性如&#xff1a;id、手机号、身份证号、学号等唯一值&#xff0c;对象列表同理 只要不对列表的逆序添加&#xff0c;逆序删除等破坏顺序的操作&#xff0c;仅用于渲染…

Mysql 递归查询子类Id的所有父类Id

文章目录 问题描述先看结果表结构展示实现递归查询集合查询结果修复数据 问题描述 最近开发过程中遇到一个问题,每次添加代理关系都要去递归查询一下它在不在这个代理关系树上.很麻烦也很浪费资源.想着把代理关系的父类全部存起来 先看结果 表结构展示 表名(t_agent_user_rela…

【nlp】3.1 Transformer背景介绍及架构

Transformer背景介绍 1 Transformer的诞生2 Transformer的优势3 Transformer的市场4 Transformer架构4.1 Transformer模型的作用4.2 Transformer总体架构图4.2.1 输入部分4.2.2 输出部分包含4.2.3 编码器部分4.2.4 解码器部分1 Transformer的诞生 2018年10月,Google发出一篇论…