2.JdbcTemplate的概述和入门
它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。spring 框架为我们提供了很多 的操作模板类。
操作关系型数据的: JdbcTemplate HibernateTemplate
操作 nosql 数据库的: RedisTemplate
操作消息队列的: JmsTemplate
我们今天的主角在 spring-jdbc-5.0.2.RELEASE.jar
中,我们在导包的时候,除了要导入这个 jar 包 外,还需要导入一个spring-tx-5.0.2.RELEASE.jar
(它是和事务相关的)
`
JdbcTemplate有什么作用:
它就是用于和数据库交互的,实现对表的CRUD的操作。
我们在学一个新的东西的时候,通常在想它有什么作用,如何创建,又有什么方法,接下来我们在程序中操作一下去明白,新建一个maven工程,配置pom.xml:首先需要spring-context依赖,还有就是刚刚说说的spring-jdbc和spring-tx的依赖,还需要一个mysql的依赖。
4.0.0
com.itheima
spring04_eesy_01jdbctemplate
1.0-SNAPSHOT
org.springframework
spring-context
5.1.2.RELEASE
mysql
mysql-connector-java
8.0.15
org.springframework
spring-jdbc
5.2.0.RELEASE
org.springframework
spring-tx
5.2.1.RELEASE
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
接下来创建实体类Account,为成员变量生成get/set方法还有toString方法。
package com.itheima.domain;
public class Account {
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
现在我们创建Jdbctemplate类
我们仍然使用我们之前为学习spring所创建的数据库eesy,我们需要创建jdbcTemplate对象,这里的方式是直接new一个出来使用,另外我们还需要配置数据源,spring内置的数据源是 DriverManagerDataSource,通过它设置Driver,url,user,password。
当我们创建好jdbcTemplate对象时,就可以使用它的excute方法来执行sql语句了,数据源可以通过jdbcTemplate的构造方法直接在new的时候就添加上,也可以用jdbcTemplate的setDataSource方法。
package com.itheima.jdbctemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class Jdbctemplate {
public static void main(String[] args) {
//准备数据源,spring的内置数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/eesy?serverTimezone=GMT%2B8&useSSL=false");
ds.setUsername("root");
ds.setPassword("password");
//创建JdbcTemplate对象
JdbcTemplate jtl = new JdbcTemplate(ds);
//jtl.setDataSource(ds);
//执行语句
jtl.execute("insert into account(name, money) value ('ccc',1000)");
}
}
这个就是JdbcTemplate的最基本的使用
现在来看,JdbcTemplate的最基本使用是有很多问题的,耦合性太强,数据源部分写死知类的问题。
3.JdbcTemplate在spring的ioc中使用
我们如果要解决以上问题,那很简单,用spring来管理他们。
通过IOC容器来创建JdbcTemplate
这里出现了一个问题:
使用原来的url时,会报出:javax.net.ssl.SSLException:closing inbound before receiving peer's close_notify
的错误,这个问题的解决办法是 配置连接数据库的url时 ,加上useSSL=false。如以下格式,注意将数据库名(db_testjpa)改为你自己的数据库名。参考来源
之前就抛出这个异常,按照网上的搜寻结果我在url后i添加了jdbc:mysql://localhost:3306/eesy?serverTimezone=GMT%2B8&useSSL=false
不过把它引用到配置文件的时候会有一个转义符的错误
&
看起来需要使用转义符解决,ALT+ENTER让IDE自动帮我解决了
其实只需要 加上useSSL=false就可以了,也能成功执行的。
&的转义是
&
4.JdbcTemplate的CRUD操作
接下来我们需要讲讲JdbcTemplate的CRUD的操作
这些都是单表的操作,多表查询不是JdbcTemplate的事,而是SQL语句的事
- 保存
//保存
jtl.update("insert into account (name, money) VALUES (?,?)","ppp",1000f);
- 更新
有了保存为基础,更新如法炮制
//更新
jtl.update("update account SET name=?,money=? WHERE id = ?","ggg",1000f,18);
- 删除
有了保存为基础,更新如法炮制
//删除
jtl.update("delete from account where id= ? ", 18);
- 查询所有
接下来是查询所有,需要用到的方法是jdbcTemplate的query()方法,query方法有很多,如何选择我们需要的query方法呢?
如何快速定位自己想要的方法?要明确两点:
- 我们有什么? SQL语句 语句的参数
- 我们要什么? 返回一个List结果集
基于这两点,所以方法里 有void 和T就不用考虑了,只考虑LIST
最后筛选出来,就只剩这两个方法,这两个方法的不同是针对于不同的JDK版本, 第一个是所有版本都可以用,第二个版本是JDK1.5以上可使用
我们已有第一个参数SQL语句,第三个参数变量,这第二个参数RowMapper是一个T类型的接口
我们无法拿来直接使用,但是我们可以写一个类来implements这个接口
/**
* 定义Account的封装策略
*/
class AccountRowMapper1 implements RowMapper{
/**
* 把结果集中的数据装到Account中,然后由spring把Account加到集合中
* @param resultSet
* @param i
* @return
* @throws SQLException
*/
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
return null;
}
}
RowMappler的T类型就写Account类型就好了,需要重载mapRow方法,这个方法是用来定义Account的封装策略的,其实就是把结果集的数据封装到Account中,然后由spring把Account加载到集合中。
(这是因为query.()方法返回的是一个List,而mapRow返回的是Account,我们只需要返回Account就行了,spring会帮我加到集合里)
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getFloat("money"));
return account;
}
当我们把AccountRowMapper写完,第二个参数就可以填上 new AccountRowMapper了
所以我们可以定义 List
List accounts = jtl.query("select * from account where money > ?",new AccountRowMapper(), 800f);
for (Account account:accounts){
System.out.println(account);
}
dbutils中的QueryRunner的query方法第一个参数也是SQL语句,第二个参数是ResultSetHandler
我们Spring也提供了这样的方法,spring提供的这个方法叫BeanProperyRowMapper
,这个方法里面的参数要加上类的字节码,可以看到运行结果是一样的
如果以后我们要把一个对象封装到集合里面,就不用自己写,直接拿spring提供的BeanProperyRowMapper使用就可以了。
- 查询一个
有了查询所有的基础,查询一个就变得更简单了,直接拿来用就完事儿了
//查询一个
List accounts = jtl.query("select * from account where id = ?",
new BeanPropertyRowMapper(Account.class), 1);
System.out.println(accounts);
- 查询返回一行一列(使用聚合函数,但不包括group by语句)
查询返回一行一列只能用queryForObject方法,也是三个参数,第一个SQL语句,第二个参数是字节码,因为我们要返回的interger类型,所以写interger.class就可以了,写第三个是我们的条件。
//查询返回一行一列(使用聚合函数,但是不加group by语句)
Integer integer = jtl.queryForObject("select count(*) from account where money = ?", Integer.class, 1000);
System.out.println(integer);
5.JdbcTemplate在Dao中的使用
CRUD就讲完了 实际上在开发中是基于Dao的,那我们还是写的稍微规范点。
创建IAccountDao接口和AccountDaoImpl,我就写了一个方法来实现。
package com.itheima.dao;
import com.itheima.domain.Account;
import java.util.List;
/**
* 账户持久层接口
*/
public interface IAccountDao {
/**
* 通过id查询账户
* @return
*/
List findAccountbyId(Integer accountId);
}
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List findAccountbyId(Integer accountId) {
List accountList = jdbcTemplate.query("select * from account where id = ?",
new BeanPropertyRowMapper(Account.class), accountId);
return accountList;
}
我们是要交给Spring来控制的,接下来来配置bean.xml
测试------------->
package com.itheima;
import com.itheima.dao.IAccountDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
@Test
public void testDao(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountDao dao = (IAccountDao) ac.getBean("accountDao");
System.out.println(dao.findAccountbyId(1));
}
}
6.JdbcDaoSupport的使用以及Dao的两种编写方式
如果我们存在很多Dao,比如productDao和OtherDao等等
那么在前面这段jdbcTemplate将会在Dao里重复出现
如何减少这种重复代码?
我们可以把JdbcTemplate抽取出来,创建JdbcSupport类,为jdbcTemplate生成set&get方法,然后让AccountDaoImpl继承jdbcSupport,用父类的getJdbcTemplate()来得到JdbcTemplate,减少代码重复。
package com.itheima.jdbctemplate;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcSupport {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
接下来在daoImpl的代码进行改动
既然JdbcSupport能够抽取jdbcTemplate,那么能不能抽取dataSource呢,我们为dataSource创建了get&set方法,并且判断,如果jdbcTemplate为空的时候,创建一个jdbcTemplate,createJdbcTemplate方法传入dataSource 通过构造方法就能new 一个JdbcTemplate返回。
package com.itheima.jdbctemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
public class JdbcSupport {
private JdbcTemplate jdbcTemplate;
private DataSource dataSource;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
if(jdbcTemplate == null){
jdbcTemplate = createJdbcTemplate(dataSource);
}
}
private JdbcTemplate createJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
此时开始配置bean.xml,把jdbcSupport扔入IOC容器,并且注入jdbcTemplate和dataSourece,并且在accountDao的注入屏蔽掉jdbcTemplate又注入dataSource时仍然能执行成功。
其实我们写的这个,Spring是为我们提供的,这个类叫JdbcDaoSupport,一样的可以去除定义和set&get的重复代码,当我们有多个dao的时候,就没有必要定义和生成get&ser方法。
所以我们dao就有两种写法,一种不继承JdbcTemplate 一种是继承了JdbcTemplate
那么这两种有什么区别呢
可以看到JdbcDaoSupport的源码是不可以打上注解的,所以当我们的dao使用注解的时候,继承jdbcDaoSupport的使用将变得麻烦。
使用注解开发建议采用不继承JdbcDaoSupport的写法,使用xml的配置就可以使用继承JdbcDaoSupport的写法。