Spring 6 JdbcClient API 指南
1. 概述
在本教程中,我們將了解JdbcClient
接口,它是 Spring Framework 6.1 的最新成員。它為JdbcTemplate
和NamedParameterJdbcTemplate
提供了一個具有統一外觀的流暢介面。這意味著現在它支援連結類型的操作。我們現在可以以流暢的 API 風格定義查詢、設定參數並執行資料庫操作。
此功能簡化了 JDBC 操作,使它們更具可讀性且更易於理解。但是,我們必須使用舊的JdbcTemplate
和NamedParameterJdbcTemplate
類別來進行 JDBC 批次作業和預存程序呼叫。
在本文中,我們將使用 H2 資料庫來展示JdbcClient
的功能。
2. 必要的資料庫設置
讓我們先來看看student
表,我們在探索JdbcClient
時將引用該表:
CREATE TABLE student (
student_id INT AUTO_INCREMENT PRIMARY KEY,
student_name VARCHAR(255) NOT NULL,
age INT,
grade INT NOT NULL,
gender VARCHAR(10) NOT NULL,
state VARCHAR(100) NOT NULL
);
-- Student 1
INSERT INTO student (student_name, age, grade, gender, state) VALUES ('John Smith', 18, 3, 'Male', 'California');
-- Student 2
INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Emily Johnson', 17, 2, 'Female', 'New York');
--More insert statements...
上面的 SQL 腳本會建立一個student
表並在其中插入記錄。
3.創建JdbcClient
Spring Boot 框架會自動發現application.properties
中的資料庫連線屬性,並在應用程式啟動期間建立JdbcClient
bean。此後, JdbcClient
bean 可以在任何類別中自動組裝。
以下是一個範例,我們在StudentDao
類別中註入JdbcClient
bean:
@Repository
class StudentDao {
@Autowired
private JdbcClient jdbcClient;
}
我們將在本文中使用StudentDao
來定義理解JdbcClient
介面的方法。
但是,介面中也有一些靜態方法,例如create(DataSource dataSource)
、 create(JdbcOperations jdbcTemplate)
和create(NamedParameterJdbcOperations jdbcTemplate)
,可以建立JdbcClient
實例。
4. 使用JdbcClient
執行資料庫查詢
如前所述, JdbcClient
是JdbcTemplate
和NamedParameterJdbcTemplate
的統一外觀。因此,我們將看看它如何支持它們。
4.1.隱式支援位置參數
在本節中,我們將討論將位置 SQL 語句參數與佔位符?
綁定的支援。 。基本上,我們將了解它如何支援JdbcTemplate
的功能。
讓我們來看看StudentDao
類別中的以下方法:
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();
}
在上面的方法中,參數grade
、 state
和gender
按照它們分配給方法param()
的順序隱式註冊。最後,當呼叫query()
方法時,將執行該語句,並像JdbcTemplate
中一樣在RowMapper
的幫助下檢索結果。
query()
方法也支援ResultSetExtractor
和RowCallbackHandler
參數。我們將在接下來的部分中看到相關範例。
有趣的是,在呼叫方法list()
之前,不會檢索到任何結果。也支援其他終端操作,例如optional()
、 set()
、 [single() ,](https://docs.spring.io/spring-framework/docs/6.1.0-SNAPSHOT/javadoc-api/org/springframework/jdbc/core/simple/JdbcClient.MappedQuerySpec.html#single())
和stream()
現在,我們來看看它是如何運作的:
@Test
void givenJdbcClient_whenQueryWithPositionalParams_thenSuccess() {
List<Student> students = studentDao.getStudentsOfGradeStateAndGenderWithPositionalParams(1, "New York", "Male");
assertEquals(6, students.size());
}
讓我們看看如何在 Varargs 的幫助下完成相同的事情:
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();
}
正如我們在上面看到的,我們將param()
方法替換為採用 Varargs 參數的params()
方法。此外,我們使用single()
方法僅檢索一筆記錄。
讓我們看看它是如何運作的:
@Test
void givenJdbcClient_whenQueryWithParamsInVarargs_thenSuccess() {
Student student = studentDao.getStudentsOfGradeStateAndGenderWithParamsInVarargs(1, "New York", "Male");
assertNotNull(student);
}
此外,方法params()
也有一個重載版本,它採用參數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();
}
除了params(List<?> values)
之外,我們還看到了optional()
方法,它傳回Optional<Student>
物件。這是上面的方法的實際應用:
@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());
}
}
4.2.透過索引明確支援位置參數
如果我們需要設定SQL語句參數的位置呢?為此,我們將使用方法**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());
}
在該方法中,明確指定了參數的位置索引。此外,我們也使用了方法query(ResultSetExtractor rse)
。
讓我們看看實際效果:
@Test
void givenJdbcClient_whenQueryWithParamsIndex_thenSuccess() {
List<Student> students = studentDao.getStudentsOfGradeStateAndGenderWithParamIndex(
1, "New York", "Male");
assertEquals(6, students.size());
}
4.3.支援帶有名稱-值對的命名參數
JdbcClient
也支援使用佔位符:
×
綁定命名 SQL 語句參數,這是NamedParameterJdbcTemplate
的功能。
param()
方法也可以將參數當作鍵值對:
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();
}
在上面的方法中,我們使用了命名參數。此外,我們也使用了query(RowCallbackHandler rch)
。讓我們看看它的實際效果:
@Test
void givenJdbcClient_whenQueryWithNamedParam_thenSuccess() {
Integer count = studentDao.getCountOfStudentsOfGradeStateAndGenderWithNamedParam(1, "New York", "Male");
assertEquals(6, count);
}
4.4.支援帶有Map
的命名參數
有趣的是,我們也可以在params(Map<String,?> paramMap)
方法中在映射中傳遞參數名稱-值對:
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. 使用JdbcClient
執行資料庫操作
就像查詢一樣, JdbcClient
也支援資料庫操作,例如建立、更新和刪除記錄。與前面的部分類似,我們也可以透過param()
和params()
方法的各種重載版本來綁定參數。因此,我們不會重複它們。
但是,為了執行 SQL 語句而不是呼叫query()
方法,我們將呼叫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;
}
上面的方法使用param(String name, Object value, int sqlType)
來綁定參數。它有一個附加的sqlType
參數來指定參數的資料類型。此外, update()
方法也會傳回受影響的行數。
讓我們看看該方法的實際效果:
@Test
void givenJdbcClient_whenInsertWithNamedParamAndSqlType_thenSuccess() {
Student student = getSampleStudent("Johny Dep", 8, 4, "Male", "New York");
assertEquals(1, studentDao.insertWithSetParamWithNamedParamAndSqlType(student));
}
在上面的方法中, getSampleStudent()
傳回一個student
物件。然後將student
物件傳遞給方法insertWithSetParamWithNamedParamAndSqlType()
以在student
表中建立新記錄。
與JdbcTemplate
類似, JdbcClient
具有update(KeyHolder generatedKeyHolder)
方法來擷取執行insert
語句時所建立的自動產生的鍵。
六,結論
在本文中,我們了解了 Spring Framework 6.1 中引入的新介面JdbcClient
。我們看到了這個介面如何執行先前由JdbcTemplate
和NamedParameterJdbcTemplate
執行的所有操作。此外,由於流暢的 API 風格,程式碼也變得更容易閱讀和理解。
與往常一樣,本文中使用的程式碼可以在 GitHub 上找到。