看一个实际需求:
如果希望使用spring框架做项目,Spring框架如何处理对数据库的操作呢?
-
方案一:使用之前的JdbcUtils类
-
方案二:spring提供了一个操作数据库(表)的功能强大的类JdbcTemplate。我们可以通过ioc容器来配置一个JdbcTemplate对象,使用它来完成对数据库表的各种操作。
1.基本介绍
JdbcTemplate APIs:下载的文档-spring-5.3.8-dist\spring-framework-5.3.8\docs\javadoc-api\index.html
- 通过Spring可以配置数据源,从而完成对数据表的操作
- JdbcTemplate 是 spring 提供的访问数据库的技术。可以将 JDBC 的常用操作封装为模板方法。
2.使用实例
需求说明:使用 Spring 的方式来完成 JdbcTemplate 配置和使用
一、搭建环境:
-
引入JdbcTemplate 需要的jar包(Spring5)
-
创建数据库spring和表monster
-- 创建数据库 | |
CREATE DATABASE spring; | |
USE spring; | |
-- 创建表monster | |
CREATE TABLE monster( | |
id INT PRIMARY KEY, | |
`name` VARCHAR(64) NOT NULL DEFAULT '', | |
skill VARCHAR(64) NOT NULL DEFAULT '' | |
)CHARSET=utf8; | |
INSERT INTO monster VALUES(100,'青牛怪','吐火'); | |
INSERT INTO monster VALUES(200,'黄袍怪','吐烟'); | |
INSERT INTO monster VALUES(300,'蜘蛛怪','吐丝'); |
二、配置DataSource
- 创建配置文件src/jdbc.properties(key值随意)
在spring的ioc容器中,可以通过属性文件给bean注入值
jdbc.user=root | |
jdbc.pwd=123456 | |
jdbc.driver=com.mysql.jdbc.Driver | |
jdbc.url=jdbc:mysql://localhost:3306/spring |
- 创建容器配置文件src/JdbcTemplate_ioc.xml
<!--引入外部的属性文件--> | |
<context:property-placeholder location="classpath:jdbc.properties"/> | |
<!--配置数据源对象-DataSource--> | |
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> | |
<!--给数据源对象配置属性值--> | |
<property name="user" value="${jdbc.user}"/> | |
<property name="password" value="${jdbc.pwd}"/> | |
<property name="driverClass" value="${jdbc.driver}"/> | |
<property name="jdbcUrl" value="${jdbc.url}"/> | |
</bean> |
- 测试连接
@Test | |
public void testDatasourceByJdbcTemplate() throws SQLException { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//因为 ComboPooledDataSource实现了 DataSource接口,这里使用接口类型来获取对象 | |
DataSource dataSource = ioc.getBean(DataSource.class); | |
Connection connection = dataSource.getConnection(); | |
System.out.println("获取到连接connection=" + connection); | |
connection.close(); | |
} |
成功连接:
- 配置 JdbcTemplate_ioc.xml,将数据源分配给 JdbcTemplate bean对象
<!--配置JdbcTemplate对象--> | |
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"> | |
<!--给JdbcTemplate对象配置DataSource属性--> | |
<property name="dataSource" ref="dataSource"/> | |
</bean> |
2.1添加数据
@Test | |
public void addDataByJdbcTemplate() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取JdbcTemplate对象 | |
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class); | |
//1.添加方式 | |
String sql = "insert into monster values(400,'红孩儿','风火轮')"; | |
jdbcTemplate.execute(sql); | |
//2.添加方式 2(推荐) | |
String sql2 = "insert into monster values(?,?,?)"; | |
//返回的 int类型 表示执行后表受影响的记录数 | |
int affected = jdbcTemplate.update(sql2, 500, "牛魔王", "芭蕉扇"); | |
System.out.println("add ok affected = " + affected); | |
} |
添加成功:
2.2修改数据
//测试通过JdbcTemplate对象完成修改数据 | |
@Test | |
public void updateDataByJdbcTemplate() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取JdbcTemplate对象 | |
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class); | |
String sql = "update monster set skill=? where id=?"; | |
int affected = jdbcTemplate.update(sql, "美人计", 300); | |
System.out.println("update is ok, affected = " + affected); | |
} |
修改成功:
2.3批量处理
对于某个类,如果有很多API,使用的步骤:
1.先确定API名字 2.根据API提供的参数,组织参数 3.根据API可以推测类似的用法和功能
//批量添加两个 monster | |
@Test | |
public void addBatchDataByJdbcTemplate() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取JdbcTemplate对象 | |
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class); | |
//1.准备参数 | |
String sql = "insert into monster values(?,?,?)"; | |
List<Object[]> batchArgs = new ArrayList<>(); | |
batchArgs.add(new Object[]{600, "白蛇", "翻江倒海"}); | |
batchArgs.add(new Object[]{700, "青蛇", "竹叶青"}); | |
//2.调用 | |
//int[] batchUpdate(String sql, List<Object[]> batchArgs); | |
//说明:返回结果为int数组,每个元素对应上面的sql语句对表的影响记录数 | |
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); | |
//输出 | |
for (int anInt : ints) { | |
System.out.println("anInt=" + anInt); | |
} | |
System.out.println("batch add is ok.."); | |
} |
批处理结果:
2.4查询
实体类 Monster.java
package com.li.bean; | |
/** | |
* @author 李 | |
* @version 1.0 | |
* Javabean / Entity | |
*/ | |
public class Monster { | |
private Integer monsterId; | |
private String name; | |
private String skill; | |
//无参构造器一定要有,spring底层反射创建对象时需要使用 | |
public Monster() { | |
} | |
//全参构造器 | |
public Monster(Integer monsterId, String name, String skill) { | |
this.monsterId = monsterId; | |
this.name = name; | |
this.skill = skill; | |
} | |
public Integer getMonsterId() { | |
return monsterId; | |
} | |
public void setMonsterId(Integer monsterId) { | |
this.monsterId = monsterId; | |
} | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
public String getSkill() { | |
return skill; | |
} | |
public void setSkill(String skill) { | |
this.skill = skill; | |
} | |
@Override | |
public String toString() { | |
return "Monster{" + | |
"monsterId=" + monsterId + | |
", name='" + name + '\'' + | |
", skill='" + skill + '\'' + | |
'}'; | |
} | |
} |
2.4.1查询单行多列
查询 id=500 的 monster 并封装到 Monster 实体对象
//查询 id=100的 monster并封装到 Monster实体对象 | |
@Test | |
public void selectDataByJdbcTemplate() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取JdbcTemplate对象 | |
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class); | |
//1.确定API | |
//<T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) | |
//2.准备参数 | |
//注意:封装对象时,如果查询返回的字段名和实体对象的属性名不一致会出现问题,最好使用别名! | |
String sql = "SELECT id AS monsterId , NAME, skill FROM monster WHERE id=?"; | |
//使用RowMapper接口来对返回的数据,进行一个封装(底层是反射->setter) | |
RowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class); | |
//3.调用 | |
Monster monster = jdbcTemplate.queryForObject(sql, rowMapper, 500); | |
System.out.println("monster=" + monster); | |
} |
查询结果:
2.4.2查询多行多列
查询 id>=200 的 monster,并封装到 Monster 实体对象
//查询 id>=200的 monster并封装到 Monster实体对象 | |
@Test | |
public void selectMulDataByJdbcTemplate() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取JdbcTemplate对象 | |
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class); | |
//1.确定API | |
//public <T> T query(String sql, ResultSetExtractor<T> rse, @Nullable Object... args) | |
//2.组织参数 | |
String sql = "SELECT id AS monsterId , NAME, skill FROM monster WHERE id>= ?"; | |
BeanPropertyRowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class); | |
List<Monster> query = jdbcTemplate.query(sql, rowMapper, 200); | |
for (Monster monster : query) { | |
System.out.println("monster=" + monster); | |
} | |
} |
查询结果:
2.4.3查询单行单列
查询返回结果只有单行单列的值,比如查询表中的总记录数,或者查询 id=200 的 name 字段的值
//查询 id>=200的 monster并封装到 Monster实体对象 | |
@Test | |
public void selectScalarByJdbcTemplate() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取JdbcTemplate对象 | |
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class); | |
//1.确定API | |
// public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) | |
//requiredType 表示返回的单行单列的 值的 数据类型 | |
//2.组织参数 | |
String sql = "SELECT NAME FROM monster WHERE id=?"; | |
String sql2 = "SELECT COUNT(*) FROM monster"; | |
//3.调用 | |
String name = jdbcTemplate.queryForObject(sql, String.class, 200); | |
Integer count = jdbcTemplate.queryForObject(sql2, Integer.class); | |
System.out.println("id=200 的 name = " + name); | |
System.out.println("monster表的总记录数 = " + count); | |
} |
查询结果:
2.5具名参数
- 什么是具名参数?
具名参数:SQL 按名称(以冒号开头)而不是按位置进行指定。具名参数更易于维护, 也提升了可读性。具名参数由框架类在运行时用占位符取代。
在 JDBC用法中,SQL参数是用占位符 ?
表示,并且受到位置的限制。定位参数的问题在于,一旦参数的位置发生变化,必须改变参数的绑定。
而在Spring JDBC中,绑定SQL参数的另一种选择是使用具名参数 (named parameter),SQL具名参数是按照名称绑定,而不是位置绑定。具名参数的命名随意。
2.5.1具名参数的使用
需求:使用map传入具名参数完成操作,比如添加 螃蟹精.:name
就是具名参数形式。
使用具名参数,需要使用 NamedParameterJdbcTemplate 类,语句形式 String sql = "insert into monster values(:my_id, :name, :skill)";
- 增加配置NameParameterJdbcTemplate对象(部分)
<!--配置NamedParameterJdbcTemplate对象--> | |
<bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" id="namedParameterJdbcTemplate"> | |
<!--通过构造器设置数据源--> | |
<constructor-arg name="dataSource" ref="dataSource"/> | |
</bean> |
- 测试
@Test | |
public void testDataByNamedParameterJdbcTemplate() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取 NamedParameterJdbcTemplate对象 | |
NamedParameterJdbcTemplate namedParameterJdbcTemplate = | |
ioc.getBean(NamedParameterJdbcTemplate.class); | |
//1.确定API | |
//public int update(String sql, Map<String, ?> paramMap) | |
//2.准备参数 [:my_id,:name,:skill] 要求按照规定的名字来设置参数,具名参数的命名随意,但是map中的key要和命名一致 | |
String sql = "insert into monster values(:my_id,:name,:skill)"; | |
HashMap<String, Object> paramMap = new HashMap<>(); | |
//给map填入具体的数据 | |
paramMap.put("my_id", 800); | |
paramMap.put("name", "孙悟空"); | |
paramMap.put("skill", "七十二变"); | |
//3.调用 | |
int affected = namedParameterJdbcTemplate.update(sql, paramMap); | |
System.out.println("add is ok , affected = " + affected); | |
} |
插入成功:
2.5.1sqlparametersource
使用 sqlparametersource 来封装具名参数,添加一个Monster
//sqlparametersource 的使用 | |
@Test | |
public void operaDataBySqlparametersource() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
//获取 NamedParameterJdbcTemplate对象 | |
NamedParameterJdbcTemplate namedParameterJdbcTemplate = | |
ioc.getBean(NamedParameterJdbcTemplate.class); | |
//1.确定API | |
// public int update(String sql, SqlParameterSource paramSource) | |
//2.准备参数 | |
//注意这里的具名参数要和对象的属性名一致 | |
String sql = "insert into monster values(:monsterId,:name,:skill)"; | |
Monster monster = new Monster(900, "乌鸡精", "吃人不吐骨头"); | |
SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(monster); | |
//3.调用 | |
int affected = namedParameterJdbcTemplate.update(sql, sqlParameterSource); | |
System.out.println("add is ok , affected = " + affected); | |
} |
添加成功:
2.5综合使用
在Dao对象中使用 JdbcTemplate完成对数据的操作
- MonsterDao
package com.li.jdbctemplate.dao; | |
import com.li.bean.Monster; | |
import org.springframework.jdbc.core.JdbcTemplate; | |
import org.springframework.stereotype.Repository; | |
import javax.annotation.Resource; | |
/** | |
* @author 李 | |
* @version 1.0 | |
*/ | |
@Repository //将MonsterDao注入到spring容器 | |
public class MonsterDao { | |
//注入一个属性 | |
@Resource | |
private JdbcTemplate jdbcTemplate; | |
//完成保存任务 | |
public void save(Monster monster) { | |
//组织 sql语句 | |
String sql = "insert into monster values(?,?,?)"; | |
int affected = jdbcTemplate.update | |
(sql, monster.getMonsterId(), monster.getName(), monster.getSkill()); | |
System.out.println("affected=" + affected); | |
} | |
} |
- 容器文件中配置要扫描的包
<!--引入外部的属性文件--> | |
<context:property-placeholder location="classpath:jdbc.properties"/> | |
<!--配置数据源对象-DataSource--> | |
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> | |
<!--给数据源对象配置属性值--> | |
<property name="user" value="${jdbc.user}"/> | |
<property name="password" value="${jdbc.pwd}"/> | |
<property name="driverClass" value="${jdbc.driver}"/> | |
<property name="jdbcUrl" value="${jdbc.url}"/> | |
</bean> | |
<!--配置JdbcTemplate对象--> | |
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"> | |
<!--给JdbcTemplate对象配置DataSource属性--> | |
<property name="dataSource" ref="dataSource"/> | |
</bean> | |
<!--配置要扫描的包--> | |
<context:component-scan base-package="com.li.jdbctemplate.dao"/> |
- 测试
//测试 MonsterDao 是否生效 | |
@Test | |
public void monsterDaoSave() { | |
//获取容器 | |
ApplicationContext ioc = | |
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml"); | |
MonsterDao monsterDao = ioc.getBean(MonsterDao.class); | |
Monster monster = new Monster(1000, "哪吒", "混天绫"); | |
monsterDao.save(monster); | |
System.out.println("MonsterDao 保存成功..."); | |
} |
插入成功: