Spring笔记11 Spring中的JdbcTemplate
coconutnut

https://www.bilibili.com/video/av47952931

p67~71

持久层总图

Spring中的JdbcTemplate

作用:和数据库交互,实现对表的CRUD操作

JdbcTemplate与DbUtils

DbUtils是Apache提供的一个对JDBC进行简单封装的开源工具类库,主要有

  • org.apache.commons.dbutils.QueryRunner — 核心类,执行SQL查询以处理结果集(线程安全)
  • org.apache.commons.dbutils.ResultSetHandler — 结果集封装器
  • org.apache.commons.dbutils.DbUtils — 提供如加载驱动、关闭连接、事务提交、回滚等常规工作的工具类

JdbcTemplate是Spring提供的一个对象,是对原始Jdbc API对象的简单封装,基本方法:

  • execute方法
  • update与batchUpdate方法
  • query与queryXXX方法
  • call方法

DbUtils导的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--        dbutils-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>

<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>

<!-- jdbc连接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>

JdbcTemplate导的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--        JdbcTemplate-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>

<!-- 和事务相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>

<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>

表的实体类

实现Serializable接口

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 账户的实体类
*/
public class Account implements Serializable {

private Integer id;
private String name;
private Float money;

// get set toString
// ...
}

JdbcTemplate的最基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JdbcTemplateDemo1 {

public static void main(String[] args) {
// 准备数据源:spring的内置数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/groot?characterEncoding=utf8");
ds.setUsername("root");
ds.setPassword("iamgroot");

// 1.创建JdbcTemplate对象
JdbcTemplate jt = new JdbcTemplate();
// 给jt设置数据源
jt.setDataSource(ds);
// 2.执行操作
jt.execute("insert into account(name,money) values('ccc',1000)");
}

}

执行,没有问题

但是这样写数据库的配置都写死了,而且用了很多set、new

都可以通过IoC配置

IoC配置

这里dataSource暂时先用Spring内置的(id=”dataSource”)

1
2
3
4
5
6
7
8
9
10
11
12
public class JdbcTemplateDemo2 {

public static void main(String[] args) {
// 1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// 2.获取对象
JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
// 3.执行操作
jt.execute("insert into account(name,money)values('ddd',2222)");
}

}

也没问题

使用JdbcTemplate实现基本的CRUD

增删改都是update方法

1
2
3
4
5
6
7
8
// 保存
jt.update("insert into account(name,money)values(?,?)","eee",3333f);

// 更新
jt.update("update account set name=?,money=? where id=?","test",4567,7);

// 删除
jt.update("delete from account where id=?",8);

查询是query

query有很多很多很多重载的方法,找的时候关注两个点:我们有什么(参数)、我们要什么(返回值)

比如有sql语句、参数,要返回一个List,筛选出来基本就剩2个了,它们一个要传Object[] args,一个是Object… args,是针对不同版本的(前者所有版本可用,后者jdk5之后支持可变参数可用)

第一个方法:

1
2
// 查询所有
List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);

需要写一个AccountRowMapper类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 定义Account的封装策略
*/
class AccountRowMapper implements RowMapper<Account> {
/**
* 把结果集中的数据封装到Account中,然后由spring把每个Account加到集合中
* @param rs
* @param rowNum
* @return
* @throws SQLException
*/
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}

此时问题来了,对比dbutils中的QueryRunner

不同的只有ResultSetHandler和AccountRowMapper,从封装的角度作用是一样的

以前是用的dbutils提供的beanListHandler实现的

难道机智的Spring没有这个实现吗还要自己写?

显然必须有 -> BeanPropertyRowMapper

1
2
3
4
5
6
// 查询所有
// List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);
List<Account> accounts = jt.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),1000f);
for(Account account : accounts){
System.out.println(account);
}

能查所有了,查一个也差不多(实际使用BeanPropertyRowMapper比较多)

1
2
3
// 查询一个
List<Account> accounts = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),3);
System.out.println(accounts.isEmpty()? "没有内容" :accounts.get(0));

还有聚合

1
2
3
// 查询返回一行一列(使用聚合函数,但不加group by子句)
Long count = jt.queryForObject("select count(*) from account where money > ?",Long.class,1000f);
System.out.println(count);

第二个参数用来指定返回类型(前提是能转,为防溢出一般用Long接收)

JdbcTemplate在Dao中的使用

写一个IAccountDao接口,实现类中用上面的方法完成功能即可

JdbcDaoSupport的使用

有一个问题是,如果实际开发中有多个Dao,每个实现类中都要获取JdbcTemplate

1
2
3
4
5
private JdbcTemplate jdbcTemplate;

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

这段代码会重复很多次

可以写一个JdbcDaoSupport类用于抽取这段重复代码

其它DaoImp继承这个类,get其中的jdbcTemplate,dataSource也在其中设置好

1
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
/**
* 此类用于抽取dao中的重复代码
*/
public class JdbcDaoSupport {

private JdbcTemplate jdbcTemplate;

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}


public void setDataSource(DataSource dataSource) {
if(jdbcTemplate == null){
jdbcTemplate = createJdbcTemplate(dataSource);
}
}

private JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}

显然,setDataSource的时候JdbcTemplate也有了

于是配置的时候就不用配JdbcTemplate了

1
2
3
4
5
6
7
8
9
10
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!-- <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
<property name="dataSource" ref="dataSource"></property>
</bean>

<!--配置JdbcTemplate-->
<!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">-->
<!-- <property name="dataSource" ref="dataSource"></property>-->
<!-- </bean>-->

那么问题又来了

自己都能写出来的东西Spring显然必须有

把这个自己写的JdbcDaoSupport注释掉,还能跑,为啥呢

1
import org.springframework.jdbc.core.support.JdbcDaoSupport;

自动导入了Spring的JdbcDaoSupport

打开这个类看一下,就有JdbcTemplate,有setDataSource方法,有上面一堆

所以不需要自己写,继承就完事

继承它的目的就是在有多个Dao时去除重复代码

但是由于源码不能动,就不好通过注解配置了,自己写和直接继承的区别就在于这里,需要权衡