SpringBoot3.2新特性之JdbcClient的使用
一、簡(jiǎn)介
Spring 6.1 中新添加了 JdbcClient 接口,它提供了 Fluent 風(fēng)格的 API,統(tǒng)一了 JdbcTemplate 和 NamedParameterJdbcTemplate 的 Facade,支持鏈?zhǔn)讲僮鳌?/p>
有了 JdbcClient 后就可以使用 Fluent 風(fēng)格的 API 定義查詢(xún)、設(shè)置參數(shù)以及執(zhí)行數(shù)據(jù)庫(kù)操作了。
該功能簡(jiǎn)化了 JDBC 操作,使其更易讀、更易懂。然而,對(duì)于 JDBC 批處理操作和存儲(chǔ)過(guò)程的調(diào)用,仍然需要使用 JdbcTemplate 和 NamedParameterJdbcTemplate 類(lèi)。
SpringBoot3.2中也集成了JdbcClient ,配置也很簡(jiǎn)單,只要引入相關(guān)包,Spring Boot 框架會(huì)自動(dòng)發(fā)現(xiàn) application.properties 中的 DB 連接屬性,并在應(yīng)用啟動(dòng)時(shí)創(chuàng)建 JdbcClient Bean。之后,JdbcClient Bean 可以在任何類(lèi)中注入、使用。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
@Component
public class DbService {
// 直接使用即可
@Autowired
private JdbcClient jdbcClient;
}
二、使用
1、支持隱式位置參數(shù)
使用占位符 ? 來(lái)通過(guò)位置綁定 SQL 參數(shù)。
List<Student> getStudentsOfGradeStateAndGenderWithPositionalParams(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = ? and state = ? and gender = ?";
return jdbcClient.sql(sql)
.param(grade)
.param(state)
.param(gender)
.query(new StudentRowMapper()).list();
}
@Test
void givenJdbcClient_whenQueryWithPositionalParams_thenSuccess() {
List<Student> students = studentDao.getStudentsOfGradeStateAndGenderWithPositionalParams(1, "New York", "Male");
assertEquals(6, students.size());
}
在上述方法中,參數(shù) grade、state 和 gender 是按照方法 param() 的調(diào)用順序隱式注冊(cè)的。最后,當(dāng)調(diào)用 query() 方法時(shí),語(yǔ)句將被執(zhí)行,并通過(guò) RowMapper 轉(zhuǎn)換、獲取結(jié)果,和 JdbcTemplate 一樣。
query() 方法還支持 ResultSetExtractor 和 RowCallbackHandler 參數(shù)。
在調(diào)用 list() 方法之前,不會(huì)檢索到任何結(jié)果。此外,還支持其他獲取結(jié)果的操作,如 optional()、set()、single() 和 stream()。
還可以通過(guò) params 方法使用可變參數(shù)來(lái)設(shè)置 SQL 參數(shù):
Student getStudentsOfGradeStateAndGenderWithParamsInVarargs(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = ? and state = ? and gender = ? limit 1";
return jdbcClient.sql(sql)
.params(grade, state, gender)
.query(new StudentRowMapper()).single();
}
@Test
void givenJdbcClient_whenQueryWithParamsInVarargs_thenSuccess() {
Student student = studentDao.getStudentsOfGradeStateAndGenderWithParamsInVarargs(1, "New York", "Male");
assertNotNull(student);
}
如上所示,使用 params() 方法替換了 param() 方法,后者使用可變參數(shù)。最后通過(guò) single() 方法來(lái)檢索一條記錄。
params() 方法還有一個(gè)重載版本,可以接收一個(gè)參數(shù) List。
Optional<Student> getStudentsOfGradeStateAndGenderWithParamsInList(List params) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = ? and state = ? and gender = ? limit 1";
return jdbcClient.sql(sql)
.params(params)
.query(new StudentRowMapper()).optional();
}
@Test
void givenJdbcClient_whenQueryWithParamsInList_thenSuccess() {
List params = List.of(1, "New York", "Male");
Optional<Student> optional = studentDao.getStudentsOfGradeStateAndGenderWithParamsInList(params);
if(optional.isPresent()) {
assertNotNull(optional.get());
} else {
assertThrows(NoSuchElementException.class, () -> optional.get());
}
}
除了 params(List<?> values),這里還通過(guò) optional() 方法返回了 Optional<Student> 對(duì)象。
2、通過(guò)索引設(shè)置位置參數(shù)
如果需要設(shè)置 SQL 語(yǔ)句參數(shù)的位置,可以使用 param(int jdbcIndex, Object value) 方法:
List<Student> getStudentsOfGradeStateAndGenderWithParamIndex(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = ? and state = ? and gender = ?";
return jdbcClient.sql(sql)
.param(1, grade)
.param(2, state)
.param(3, gender)
.query(new StudentResultExtractor());
}
@Test
void givenJdbcClient_whenQueryWithParamsIndex_thenSuccess() {
List<Student> students = studentDao.getStudentsOfGradeStateAndGenderWithParamIndex(
1, "New York", "Male");
assertEquals(6, students.size());
}
在該方法中,明確指定了參數(shù)的位置。此外,還使用了 query(ResultSetExtractor rse) 方法。
3、支持 Name / Value 對(duì)命名參數(shù)
JdbcClient 還支持使用占位符 : 綁定命名的 SQL 語(yǔ)句參數(shù),這是 NamedParameterJdbcTemplate 的特性。
param() 方法也可以設(shè)置鍵值對(duì)類(lèi)型的參數(shù):
int getCountOfStudentsOfGradeStateAndGenderWithNamedParam(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = :grade and state = :state and gender = :gender";
RowCountCallbackHandler countCallbackHandler = new RowCountCallbackHandler();
jdbcClient.sql(sql)
.param("grade", grade)
.param("state", state)
.param("gender", gender)
.query(countCallbackHandler);
return countCallbackHandler.getRowCount();
}
@Test
void givenJdbcClient_whenQueryWithNamedParam_thenSuccess() {
Integer count = studentDao.getCountOfStudentsOfGradeStateAndGenderWithNamedParam(1, "New York", "Male");
assertEquals(6, count);
}
在上述方法中,使用了命名參數(shù)。此外,還使用了 query(RowCallbackHandler rch) 方法來(lái)獲取結(jié)果。
4、通過(guò) Map 設(shè)置命名參數(shù)
也可以通過(guò) params(Map<String,?> paramMap) 方法,使用 Map 對(duì)象來(lái)設(shè)置命名參數(shù):
List<Student> getStudentsOfGradeStateAndGenderWithParamMap(Map<String, ?> paramMap) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = :grade and state = :state and gender = :gender";
return jdbcClient.sql(sql)
.params(paramMap)
.query(new StudentRowMapper()).list();
}
@Test
void givenJdbcClient_whenQueryWithParamMap_thenSuccess() {
Map<String, ?> paramMap = Map.of(
"grade", 1,
"gender", "Male",
"state", "New York"
);
List<Student> students = studentDao.getStudentsOfGradeStateAndGenderWithParamMap(paramMap);
assertEquals(6, students.size());
}
5、使用 JdbClient 執(zhí)行更新操作
與查詢(xún)一樣,JdbcClient 也支持創(chuàng)建、更新和刪除記錄等數(shù)據(jù)庫(kù)操作。與前面的章節(jié)類(lèi)似,可以通過(guò)各種重載版本的 param() 和 params() 方法綁定參數(shù)。因此,這里不再贅述。
不過(guò),在執(zhí)行 INSERT、UPDATE 和 DELETE SQL 語(yǔ)句時(shí),不調(diào)用 query() 方法,而是調(diào)用 update() 方法。
在 student 表中插入記錄:
Integer insertWithSetParamWithNamedParamAndSqlType(Student student) {
String sql = "INSERT INTO student (student_name, age, grade, gender, state)"
+ "VALUES (:name, :age, :grade, :gender, :state)";
Integer noOfrowsAffected = this.jdbcClient.sql(sql)
.param("name", student.getStudentName(), Types.VARCHAR)
.param("age", student.getAge(), Types.INTEGER)
.param("grade", student.getGrade(), Types.INTEGER)
.param("gender", student.getStudentGender(), Types.VARCHAR)
.param("state", student.getState(), Types.VARCHAR)
.update();
return noOfrowsAffected;
}
@Test
void givenJdbcClient_whenInsertWithNamedParamAndSqlType_thenSuccess() {
Student student = getSampleStudent("Johny Dep", 8, 4, "Male", "New York");
assertEquals(1, studentDao.insertWithSetParamWithNamedParamAndSqlType(student));
}
上述方法使用 param(String name, Object value, int sqlType) 綁定參數(shù)。它有一個(gè)額外的 sqlType 參數(shù),用于指定參數(shù)的數(shù)據(jù)類(lèi)型。最后,update() 方法還會(huì)返回受影響的行數(shù)。
在上述方法中,getSampleStudent() 返回一個(gè) Student 對(duì)象。然后將 Student 對(duì)象傳遞給 insertWithSetParamWithNamedParamAndSqlType() 方法,在 student 表中創(chuàng)建一條新記錄。
與 JdbcTemplate 一樣,JdbcClient 也有 update(KeyHolder generatedKeyHolder) 方法,用于獲取插入記錄時(shí)生成的自增ID。
6、使用示例
// 按主鍵查詢(xún)
public MyData findDataById(Long id) {
return jdbcClient.sql("select * from my_data where id = ?")
.params(id)
.query(MyData.class)
.single();
}
// 自定義條件查詢(xún)
public List<MyData> findDataByName(String name) {
return jdbcClient.sql("select * from my_data where name = ?")
.params(name)
.query(MyData.class)
.list();
}
// 變量占位符查詢(xún)
public Integer insertDataWithNamedParam(MyData myData) {
Integer rowsAffected = jdbcClient.sql("insert into my_data values(:id,:name) ")
.param("id", myData.id())
.param("name", myData.name())
.update();
return rowsAffected;
}
// Map變量查詢(xún)
public List<MyData> findDataByParamMap(Map<String, ?> paramMap) {
return jdbcClient.sql("select * from my_data where name = :name")
.params(paramMap)
.query(MyData.class)
.list();
}
// 當(dāng)查詢(xún)返回的結(jié)果不能簡(jiǎn)單的映射到一個(gè)類(lèi)時(shí),可以編寫(xiě)RowMapper,適用于SQL語(yǔ)句比較復(fù)雜的場(chǎng)景:
public List<MyData> findDataWithRowMapper() {
return jdbcClient.sql("select * from my_data")
.query((rs, rowNum) -> new MyData(rs.getLong("id"), rs.getString("name")))
.list();
}
// 查詢(xún)記錄數(shù)
public Integer countByName(String name) {
return jdbcClient.sql("select count(*) from my_data where name = ?")
.params(name)
.query(Integer.class)
.single();
}
// 占位符插入
public Integer insertDataWithParam(MyData myData) {
Integer rowsAffected = jdbcClient.sql("insert into my_data values(?,?) ")
.param(myData.id())
.param(myData.name())
.update();
return rowsAffected;
}
// 命名參數(shù)插入
public Integer insertDataWithNamedParam(MyData myData) {
Integer rowsAffected = jdbcClient.sql("insert into my_data values(:id,:name) ")
.param("id", myData.id())
.param("name", myData.name())
.update();
return rowsAffected;
}
// 插入整個(gè)對(duì)象
public Integer insertDataWithObject(MyData myData) {
Integer rowsAffected = jdbcClient.sql("insert into my_data values(:id,:name) ")
.paramSource(myData)
.update();
return rowsAffected;
}
到此這篇關(guān)于SpringBoot3.2新特性之JdbcClient的使用的文章就介紹到這了,更多相關(guān)SpringBoot JdbcClient內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于maven環(huán)境的安裝及maven集成idea環(huán)境的問(wèn)題
Maven 是一個(gè)基于 Java 的工具,所以要做的第一件事情就是安裝 JDK。本文重點(diǎn)給大家介紹關(guān)于maven環(huán)境的安裝及和idea環(huán)境的集成問(wèn)題,感興趣的朋友一起看看吧2021-09-09
在SpringBoot中配置MySQL數(shù)據(jù)庫(kù)的詳細(xì)指南
在 Spring Boot 中配置數(shù)據(jù)庫(kù)是一個(gè)相對(duì)簡(jiǎn)單的過(guò)程,通常涉及到以下幾個(gè)步驟:添加數(shù)據(jù)庫(kù)驅(qū)動(dòng)依賴(lài)、配置數(shù)據(jù)源屬性、以及可選的配置 JPA(如果使用),下面是小編給大家編寫(xiě)的一個(gè)詳細(xì)的指南,以MySQL 數(shù)據(jù)庫(kù)為例,需要的朋友可以參考下2024-12-12
Java調(diào)用windows系統(tǒng)的CMD命令并啟動(dòng)新程序
本文教你如何使用java程序調(diào)用windows系統(tǒng)的CMD命令啟動(dòng)新程序方法,需要的朋友可以參考下2023-05-05
Java元組類(lèi)型javatuples使用實(shí)例
這篇文章主要介紹了Java元組類(lèi)型javatuples使用實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Thymeleaf 3.0 自定義標(biāo)簽方言屬性的實(shí)例講解
這篇文章主要介紹了Thymeleaf 3.0 自定義標(biāo)簽方言屬性的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
java中double轉(zhuǎn)化為BigDecimal精度缺失的實(shí)例
下面小編就為大家?guī)?lái)一篇java中double轉(zhuǎn)化為BigDecimal精度缺失的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03

