JDBCTemplate
# 40.JDBCTemplate
JDBC Template 是用来简化我们的代码的。例如我们只关心 SQL 怎么写,怎么执行,但是封装为对象的时候要一个个手工封装,非常麻烦。
# JDBC 存在的问题
即使我们使用了连接池和工具类,可以很方便地获取连接、释放连接。
但是在操作数据库时还是存在问题:每一次操作数据库(增删改查),都要定义 SQL,设置参数,执行操作,处理结果;特别是处理结果这里非常麻烦,要一步步地封装为对象(参考 ResultSet 结果封装为对象的时候)。
其实我们应该只关心 SQL 怎么写,怎么封装成对象,这纯粹是体力活,没什么技术含量。为此,有个叫 Spring 的框架对 JDBC 做了简单的封装,提供了一个 JDBCTemplate 对象简化 JDBC 的开发。
# 使用 JDBCTemplate
这里先整体说下使用步骤:
导入 jar 包
创建 JdbcTemplate 对象,依赖于数据源 DataSource:
JdbcTemplate template = new JdbcTemplate(ds);
调用
JdbcTemplate
的方法来完成 CRUD 的操作:update()
:执行 DML 语句。增、删、改语句queryForMap()
:查询结果将结果集封装为 map 集合,将列名作为 key,将值作为 value 将这条记录封装为一个 map 集合。注意:这个方法查询的结果集长度只能是 1queryForList()
:查询结果将结果集封装为 list 集合。注意:将每一条记录封装为一个 Map 集合,再将 Map 集合装载到 List 集合中query()
:查询结果,将结果封装为 JavaBean 对象。query 的参数:RowMapper- 一般我们使用 BeanPropertyRowMapper 实现类。可以完成数据到 JavaBean 的自动封装
- new BeanPropertyRowMapper <类型>(类型.class)
queryForObject
:查询结果,将结果封装为对象。一般用于聚合函数的查询
# 导入依赖
总共需要 5 个 jar 包:
commons-logging-1.2.jar
spring-beans-5.0.0.RELEASE.jar
spring-core-5.0.0.RELEASE.jar
spring-jdbc-5.0.0.RELEASE.jar
spring-tx-5.0.0.RELEASE.jar
可以从我的 GitHub 仓库里下载 jar 包:
Gitee:lib · /LearnJavaEE - Gitee (opens new window)
GitHub:LearnJavaEE/lib at master · Peter-JXL/LearnJavaEE (opens new window)
或者去官网 Spring | Home (opens new window) 下载。
# JdbcTemplate 基本使用
举个例子,我们要更新一个 SQL,可以这样做:
JdbcTemplate template = new JdbcTemplate(DruidUtils.getDataSourse());
String sql = "update students set name = ? where id = ?";
int count = template.update(sql, "PeterJXL", 1);
System.out.println(count);
2
3
4
我们不用再申请和释放连接了!我们只需要关心 SQL 语句怎么写,怎么赋值。
懂得 JdbcTemplate 的基本使用后,我们做一些练习:
- 修改 1 号学生的 name 为 caixukun
- 添加一条记录
- 删除刚才添加的记录
- 查询 id 为 1 的记录,将其封装为 Map 集合
- 查询所有记录,将其封装为 List
- 查询所有记录,将其封装为 Student 对象的 List 集合(Student 类我们在 ResultSet 结果封装为对象创建过了,这里不再赘述)
- 查询总记录数
每个小点我们用一个方法来完成,由于要测试的方法比较多,我们使用 Junit 框架
# JdbcTemplate 执行 DML 语句
这里先演示前 3 个方法:
- 修改 1 号学生的 name 为 caixukun
- 添加一条记录
- 删除刚才添加的记录
package chapter2JDBC;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.*;
public class JDBCDemo17TemplateDML {
private JdbcTemplate template = new JdbcTemplate(DruidUtils.getDataSourse());
@Test
public void testUpdateStuname(){
String sql = "update students set name = ? where id = ?";
int caixukun = template.update(sql, "caixukun", 1);
System.out.println(caixukun);
}
@Test
public void testInsertStu(){
String sql = "insert into students (id, grade, name, gender, score) values (?, ?,?,?,?)";
int caixukun = template.update(sql, 666, 1, "caixukun", 1, 99);
System.out.println(caixukun);
}
@Test
public void testDelStu(){
String sql = "delete from students where id = ?";
int count = template.update(sql,666);
System.out.println(count);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# JdbcTemplate 执行 DQL 语句
继续完成以下练习:
- 查询 id 为 1 的记录,将其封装为 Map 集合
- 查询所有记录,将其封装为 List
- 查询所有记录,将其封装为 Student 对象的 List 集合(Student 类我们在 ResultSet 结果封装为对象创建过了,这里不再赘述)
- 查询总记录数
# queryForMap
:查询一条数据并封装
queryForMap()
:查询结果将结果集封装为 map 集合,将列名作为 key,将值作为 value 将这条记录封装为一个 map 集合。注意:这个方法查询的结果集长度只能是 1
@Test
public void testSelect1(){
String sql = "select * from students where id = ?";
Map<String, Object> stringObjectMap = template.queryForMap(sql, 1);
System.out.println(stringObjectMap);
}
2
3
4
5
6
运行结果:
{id=1, name=caixukun, gender=true, grade=1, score=88}
注意:queryForMap()
只能封装一条数据的情况,如果有多条数据或者 0 条,则会报错,假设我们这样写:
String sql = "select * from students";
Map<String, Object> stringObjectMap = template.queryForMap(sql);
System.out.println(stringObjectMap);
2
3
运行结果:
org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 1, actual 13
......
2
3
# queryForList
:封装为 list 集合
queryForList()
:查询结果将结果集封装为 list 集合。注意:将每一条记录封装为一个 Map 集合,再将 Map 集合装载到 List 集合中
public void testSelect1(){
String sql = "select * from students where id = ?";
Map<String, Object> stringObjectMap = template.queryForMap(sql, 1);
System.out.println(stringObjectMap);
}
2
3
4
5
运行结果:
{id=1, name=caixukun, gender=true, grade=1, score=88}
{id=2, name=小红, gender=true, grade=1, score=95}
{id=3, name=小军, gender=false, grade=1, score=93}
{id=4, name=小白, gender=false, grade=1, score=100}
{id=5, name=小牛, gender=true, grade=2, score=96}
{id=6, name=小兵, gender=true, grade=2, score=99}
{id=7, name=小强, gender=false, grade=2, score=86}
{id=8, name=小乔, gender=false, grade=2, score=79}
{id=9, name=小青, gender=true, grade=3, score=85}
{id=10, name=小王, gender=true, grade=3, score=90}
{id=11, name=小林, gender=false, grade=3, score=91}
{id=12, name=小贝, gender=false, grade=3, score=77}
{id=1011, name=caixukun, gender=true, grade=1, score=99}
2
3
4
5
6
7
8
9
10
11
12
13
# query
:封装对象
query()
:查询结果,将结果封装为 JavaBean 对象。query 的参数:RowMapper 对象
- 一般我们使用 BeanPropertyRowMapper 实现类,可以完成数据到 JavaBean 的自动封装。
- 例如:new BeanPropertyRowMapper <类型>(类型.class)
我们自己实现 RowMapper 匿名内部类的方法来实现数据到对象的封装:
@Test
public void testQuery(){
String sql = "select * from students ";
List<Student> stus = template.query(sql, new RowMapper<Student>() {
@Override
public Student mapRow(ResultSet rs, int i) throws SQLException {
Student stu = new Student();
int id = rs.getInt("id");
String name = rs.getString("name");
int gender = rs.getInt("gender");
int grade = rs.getInt("grade");
int score = rs.getInt("score");
stu.setId(id);
stu.setName(name);
stu.setGender(gender);
stu.setGrade(grade);
stu.setScore(score);
return stu;
}
});
for (Student stu : stus) {
System.out.println(stu);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
但这样好像并没简化多少代码?我们之前也是这样写的。先别急,我们可以用默认的实现类,而不是自己实现 RowMapper 匿名内部类,简化了大量的代码:
@Test
public void testQuery2(){
String sql = "select * from students ";
List<Student> stus = template.query(sql, new BeanPropertyRowMapper<>(Student.class));
for (Student stu : stus) {
System.out.println(stu);
}
}
2
3
4
5
6
7
8
我们来看看 BeanPropertyRowMapper
的源码:可以看到 BeanPropertyRowMapper
已经实现了 RowMapper 接口。
public class BeanPropertyRowMapper<T> implements RowMapper<T> {
# queryForObject
:查询总记录数
queryForObject
:查询结果,将结果封装为对象。一般用于聚合函数的查询
@Test
public void testQuerySum(){
String sql = "select count(id) from students ";
Long aLong = template.queryForObject(sql, Long.class);
System.out.println(aLong);
}
2
3
4
5
6
第二个参数的意思是,返回的类型要用什么类型来接受,这里我们用 Long。
# 总结
使用 JDBCTemplate,可以大大简化我们的代码,我们只需关心 SQL 怎么写。