SpringBoot3.2新特性之JdbcClient的使用
作者:秃了也弱了。
一、简介
Spring 6.1
中新添加了 JdbcClient 接口,它提供了 Fluent 风格的 API,统一了 JdbcTemplate 和 NamedParameterJdbcTemplate 的 Facade,支持链式操作。
有了 JdbcClient 后就可以使用 Fluent 风格的 API 定义查询、设置参数以及执行数据库操作了。
该功能简化了 JDBC 操作,使其更易读、更易懂。然而,对于 JDBC 批处理操作和存储过程的调用,仍然需要使用 JdbcTemplate 和 NamedParameterJdbcTemplate 类。
SpringBoot3.2中也集成了JdbcClient ,配置也很简单,只要引入相关包,Spring Boot 框架会自动发现 application.properties 中的 DB 连接属性
,并在应用启动时创建 JdbcClient Bean。之后,JdbcClient Bean 可以在任何类中注入、使用。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency>
@Component public class DbService { // 直接使用即可 @Autowired private JdbcClient jdbcClient; }
二、使用
1、支持隐式位置参数
使用占位符 ?
来通过位置绑定 SQL 参数。
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()); }
在上述方法中,参数 grade、state 和 gender 是按照方法 param() 的调用顺序隐式注册的。最后,当调用 query() 方法时,语句将被执行,并通过 RowMapper 转换、获取结果,和 JdbcTemplate 一样。
query() 方法还支持 ResultSetExtractor 和 RowCallbackHandler 参数。
在调用 list() 方法之前,不会检索到任何结果。此外,还支持其他获取结果的操作,如 optional()、set()、single() 和 stream()。
还可以通过 params 方法使用可变参数来设置 SQL 参数:
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() 方法,后者使用可变参数。最后通过 single() 方法来检索一条记录。
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(); } @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)
,这里还通过 optional() 方法返回了 Optional<Student>
对象。
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()); } @Test void givenJdbcClient_whenQueryWithParamsIndex_thenSuccess() { List<Student> students = studentDao.getStudentsOfGradeStateAndGenderWithParamIndex( 1, "New York", "Male"); assertEquals(6, students.size()); }
在该方法中,明确指定了参数的位置。此外,还使用了 query(ResultSetExtractor rse) 方法。
3、支持 Name / Value 对命名参数
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(); } @Test void givenJdbcClient_whenQueryWithNamedParam_thenSuccess() { Integer count = studentDao.getCountOfStudentsOfGradeStateAndGenderWithNamedParam(1, "New York", "Male"); assertEquals(6, count); }
在上述方法中,使用了命名参数。此外,还使用了 query(RowCallbackHandler rch) 方法来获取结果。
4、通过 Map 设置命名参数
也可以通过 params(Map<String,?> paramMap)
方法,使用 Map 对象来设置命名参数:
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 执行更新操作
与查询一样,JdbcClient 也支持创建、更新和删除记录等数据库操作。与前面的章节类似,可以通过各种重载版本的 param() 和 params() 方法绑定参数。因此,这里不再赘述。
不过,在执行 INSERT、UPDATE 和 DELETE 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; } @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) 绑定参数。它有一个额外的 sqlType 参数,用于指定参数的数据类型。最后,update() 方法还会返回受影响的行数。
在上述方法中,getSampleStudent() 返回一个 Student 对象。然后将 Student 对象传递给 insertWithSetParamWithNamedParamAndSqlType() 方法,在 student 表中创建一条新记录。
与 JdbcTemplate 一样,JdbcClient 也有 update(KeyHolder generatedKeyHolder) 方法,用于获取插入记录时生成的自增ID。
6、使用示例
// 按主键查询 public MyData findDataById(Long id) { return jdbcClient.sql("select * from my_data where id = ?") .params(id) .query(MyData.class) .single(); } // 自定义条件查询 public List<MyData> findDataByName(String name) { return jdbcClient.sql("select * from my_data where name = ?") .params(name) .query(MyData.class) .list(); } // 变量占位符查询 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变量查询 public List<MyData> findDataByParamMap(Map<String, ?> paramMap) { return jdbcClient.sql("select * from my_data where name = :name") .params(paramMap) .query(MyData.class) .list(); } // 当查询返回的结果不能简单的映射到一个类时,可以编写RowMapper,适用于SQL语句比较复杂的场景: public List<MyData> findDataWithRowMapper() { return jdbcClient.sql("select * from my_data") .query((rs, rowNum) -> new MyData(rs.getLong("id"), rs.getString("name"))) .list(); } // 查询记录数 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; } // 命名参数插入 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; } // 插入整个对象 public Integer insertDataWithObject(MyData myData) { Integer rowsAffected = jdbcClient.sql("insert into my_data values(:id,:name) ") .paramSource(myData) .update(); return rowsAffected; }
到此这篇关于SpringBoot3.2新特性之JdbcClient的使用的文章就介绍到这了,更多相关SpringBoot JdbcClient内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!