JdbcTemplate 中废弃的 query(...) 和 queryForObject(...) 方法
1、概览
在 Spring Boot 2.4.x 以后,JdbcTemplate
中有几个方法注解了 @Deprecated
,也就是说被废弃了:
query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse)
query(String sql, @Nullable Object[] args, RowCallbackHandler rch)
query(String sql, @Nullable Object[] args, RowMapper<T> rowMapper)
queryForObject(String sql, @Nullable Object[] args, RowMapper<T> rowMapper)
;queryForObject(String sql, @Nullable Object[] args, Class<T> requiredType)
queryForList(String sql, @Nullable Object[] args, Class<T> elementType)
这些过时的方法都使用对象数组 Object[] args
传递参数。
JdbcTemplate
又提供了一些新的方法来代替它们,新方法使用了 “可变参数”,即 Varargs
传递参数。
本文接下来会讲解一下新旧方法的用法和区别。
2、数据库
使用 H2 内存数据库进行演示,假如我们有一张 student
表,如下:
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');
-- 其他 insert 语句 ...
3、废弃的 query()
方法
在 JdbcTemplate
中,有三种不同的 query()
方法,每种方法都使用不同的函数接口来收集或处理查询结果中的记录。如:ResultSetExtractor
、RowCallbackHandler
和 RowMapper
。
3.1、使用 RowMapper
参数的 query()
query()
方法返回 List
,表示查询数据库返回的记录。
例如:根据 age
和 gender
检索 student
表。
public List<Student> getStudentsOfAgeAndGender(Integer age, String gender) {
String sql = "select student_id, student_name, age, gender, grade from student where age= ? and gender = ?";
Object[] args = {age, gender};
return jdbcTemplate.query(sql, args, new StudentRowMapper());
}
在上述方法中,Object[]
类型的变量 args
存储了查询参数。即使是单个查询参数,也需要添加到数组中,这很不方便。哪怕是只有一个参数,也要声明一个数组。
如果需要新增一个 grade
过滤参数的话,就比较麻烦,可能得重写声明一个方法:
public List<Student> getStudentsOfAgeGenderAndGrade(Integer age, String gender, Integer grade) {
String sql = "select student_id, student_name, age, gender, grade from student where age= ? and gender = ? and grade = ?";
Object[] args = {age, gender, grade};
return jdbcTemplate.query(sql, args, new StudentRowMapper());
}
但是,如果使用“可变参数”版本的新方法,就比较灵活了,如下。
public List<Student> getStudentsOfAgeGenderAndGrade(Integer age, String gender, String grade) {
String sql = "select student_id, student_name, age, gender, grade from student where age= ? and gender = ? and grade = ?";
return jdbcTemplate.query(sql, new StudentRowMapper(), age, gender, grade);
}
有了可变参数,不需要声明 Object[] args
对象。
剩下的方法都是同理,就不详细解释了。
3.2、使用 ResultSetExtractor
参数的 query()
方法
它需要一个实现了 ResultSetExtractor
接口的实现,用于把查询结果 ResultSet
封装为 List<T>
对象。
创建一个 StudentResultExtractor
,如下:
public class StudentResultExtractor implements ResultSetExtractor<List<Student>> {
@Override
public List<Student> extractData(ResultSet rs) throws SQLException {
List<Student> students = new ArrayList<Student>();
while(rs.next()) {
Student student = new Student();
student.setStudentId(rs.getInt("student_id"));
student.setStudentName(rs.getString("student_name"));
student.setAge(rs.getInt("age"));
student.setStudentGender(rs.getString("gender"));
student.setGrade(rs.getInt("grade"));
student.setState(rs.getString("state"));
students.add(student);
}
return students;
}
}
废弃版本的用法,也就是使用 Object[] args
传递参数:
public List<Student> getStudentsOfGradeAndState(Integer grade, String state) {
String sql = "select student_id, student_name, age, gender, grade, state from student where grade = ? and state = ?";
Object[] args = {grade, state};
return jdbcTemplate.query(sql, args, new StudentResultExtractor());
}
使用新的 代替方法,使用“可变参数”:
public List<Student> getStudentsOfGradeAndState(Integer grade, String state) {
String sql = "select student_id, student_name, age, gender, grade, state from student where grade = ? and state = ?";
return jdbcTemplate.query(sql, new StudentResultExtractor(), grade, state);
}
3.3、使用 RowCallbackHandler
参数的 query()
方法
该方法,需要一个 RowCallbackHandler
接口的实现,作为查询结果的回调。
在这里我们使用 RowCountCallbackHandler
实现,用于统计结果行数。
废弃的版本 的用法如下:
public Integer getCountOfStudentsInAGradeFromAState(String grade, String state) {
String sql = "select student_id, student_name, age, gender, grade, state from student where grade = ? and state = ?";
Object[] args = {grade, state};
RowCountCallbackHandler countCallbackHandler = new RowCountCallbackHandler();
jdbcTemplate.query(sql, args, countCallbackHandler);
return countCallbackHandler.getRowCount();
}
新的 代替方法,用法如下:
public Integer getCountOfStudentsInAGradeFromAState(String grade, String state) {
String sql = "select student_id, student_name, age, gender, grade, state from student where grade = ? and state = ?";
RowCountCallbackHandler countCallbackHandler = new RowCountCallbackHandler();
jdbcTemplate.query(sql, countCallbackHandler, grade, state);
return countCallbackHandler.getRowCount();
}
4、废弃的 queryForObject()
方法
queryForObject()
有两个已废弃的重载,均使用 Object[]
传递查询参数。
queryForObject(String sql, Object[] args, Class<T> requiredType)
queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)
4.1、使用 RowMapper
参数的 queryForObject()
方法
与 query()
方法不同,queryForObject()
方法用于处理单行的数据库查询结果。
先看看下面这个已废弃版本 queryForObject()
的用法:
public Student getStudentOfStudentIDAndGrade(Integer studentID, Integer grade) {
String sql = "select student_id, student_name, age, gender, grade from student where student_id = ? and grade = ?";
Object[] args = {studentID, grade};
return jdbcTemplate.queryForObject(sql, args, new StudentRowMapper());
}
如上,和废弃的 query
方法一样,也需要为查询参数声明一个 Object[]
类型的变量。
使用新的代替方法,如下:
public Student getStudentOfStudentIDAndGrade(Integer studentID, Integer grade) {
String sql = "select student_id, student_name, age, gender, grade from student where student_id = ? and grade = ?";
return jdbcTemplate.queryForObject(sql, new StudentRowMapper(), studentID, grade);
}
使用可变参数,更加简洁了。
4.2、使用 Class<T>
参数的 queryForObject()
方法
废弃版本的用法如下:
public Integer getCountOfGenderInAGrade(String gender, Integer grade) {
String sql = "select count(1) as total from student where gender = ? and grade = ?";
Object[] args = {gender, grade};
return jdbcTemplate.queryForObject(sql, args, Integer.class);
}
使用新版本的代替方法,如下:
public Integer getCountOfGenderInAGrade(String gender, Integer grade) {
String sql = "select count(1) as total from student where gender = ? and grade = ?";
return jdbcTemplate.queryForObject(sql, Integer.class, gender, grade);
}
5、总结
JdbcTemplate
废弃了一些使用 Object[]
作为参数的 query(...)
和 queryForObject(...)
方法,同时提供了新的代替方法。新方法使用 “可变参数”,使得传递 SQL 变量参数更加的简洁、优雅。
参考:https://www.baeldung.com/spring-boot-replace-deprecated-jdbctemplate-queryforobject-query