1.jdbcTemplate的execute和update方法
package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
/*
* JdbcTemplate:是spring的框架的一部分,spring框架是工具箱。作用: 简化jdbc代码编写
* #使用:1. 数据库的操作
* 1. DDL : create/drop/alter/truncate
* 2. DML : insert/delete/update
* 3. DQL : select
* 4. DCL : transaction/权限
* 2. 核心API
* 核心类: JdbcTemplate。构造方法:JdbcTemplate(DataSource ds)
* 核心方法: A. void execute : 理论上可以执行任意sql,适合执行DDL,因为void无返回值
* B. int update : 适合执行DML
* C. 多种多样 query : 适合执行DQL,返回多种多样
*/
public class TemplateDemo01 {
@Test
public void execute(){
String sql = "create table student(id int primary key auto_increment,name varchar(20),age int)";
DataSource ds = JdbcUtil.getDs(); //拿到连接池
JdbcTemplate template = new JdbcTemplate(ds);
template.execute(sql); //无返回值
System.out.println("执行结束");
}
@Test
public void update01(){
String sql = "insert into student values(null,?,?),(null,?,?)";
JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
/*
* int update(String sql, Object... args)
* Object... args:
* 1. Object原因是参数类型是不确定的 -> Object
* 2. ... 原因是参数个数不确定
* 返回值: 被影响的行数
*/
int count = template.update(sql, "zs", 18, "ls", 19);
System.out.println(count); //2
}
@Test
public void update02(){
String sql = "update student set age = ? where id = ?";
JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
Object[] args = {
99,1}; //可变参数本质是数组
int update = template.update(sql, args);
System.out.println(update);
}
}
2.queryForXX方法
一行map是一个对象,query方法用的是多个对象这个。
package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
import java.util.Map;
import java.util.Set;
/*
* C. 多种多样 query : 适合执行DQL
* 1. queryForXX : XX表示返回值类型
* a. queryForObject
* b. queryForMap
* c. queryForList
*
* 2. query(RowMapper 行映射器)
*/
public class TemplateDemo02 {
JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
@Test
public void queryForObject01(){
String sql = "select count(*) from student";
/*
* <T> T queryForObject(String sql, Class<T> requiredType)
* requiredType : 返回值类型 -> Class对象
*
* EmptyResultDataAccessException : 空结果异常
* 查询不到任何数据,会报这个错
*/
Integer count = template.queryForObject(sql, Integer.class);
System.out.println(count);
}
@Test
public void queryForObject02(){
String sql = "select name from student where id = ?";
String s = null;
try {
s = template.queryForObject(sql, String.class,3); //3传入上面?
} catch (DataAccessException e) {
e.printStackTrace();
System.out.println("查询不到任何结果");
}
System.out.println(s);
}
@Test
public void queryForMap(){
String sql = "select * from student where id = ?";
Map<String, Object> map = template.queryForMap(sql, 1);
// System.out.println(map); //{id=1,name=zs,age=99}
Set<Map.Entry<String, Object>> entrySet = map.entrySet();
for (Map.Entry<String, Object> entry : entrySet) {
String key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + "=" + value); //竖着打印id=1 name=zs age=99
}
}
@Test
public void queryForList(){
String sql = "select * from student";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> map : list) {
System.out.println(map); //{id=1,name=zs,age=99} 换行 {id=2,name=ls,age=19}
}
}
}
3.query方法
package com.itheima01.template;
/*
* JavaBean三要素:
* 1. private属性,属性名和表中字段名是一致的!!! 都是引用类型(因为数据库中空为null,不是0)
* 2. public get set方法
* 3. public 空参构造
*/
public class Student {
private Integer id;
private String name;
private Integer age;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class TemplateDemo03 {
JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
@Test
public void query01(){
String sql = "select * from student";
/*
* List<T> query(String sql, RowMapper<T> rm)
* RowMapper : 行映射器 (接口)。方法参数中有接口类型, 那么调用的时候必须传入接口的实现类对象
*/
RowMapper<Student> rowMapper = new RowMapper<Student>() {
/*
* 如下Student mapRow(ResultSet resultSet, int i)
* 映射行 : resultSet 转换为 Student
* 1. resultSet : 结果集(每行)
* 2. i : 当前的行索引(没什么用)
*/
@Override
public Student mapRow(ResultSet resultSet, int i) throws SQLException {
String name = resultSet.getString("name");
int id = resultSet.getInt("id");
int age = resultSet.getInt("age");
Student s = new Student();
s.setId(id);
s.setName(name);
s.setAge(age);
System.out.println(i);
return s;
}
};
//RowMapper行映射器 像 动态代理中 InvocationHandler调用处理器,mapRow方法像invoke方法
List<Student> list = template.query(sql, rowMapper);
System.out.println(list);
//打印出[Student{id=1,name='zs',age=99},Student{id=2,name='ls',age=19}]
}
@Test
public void query02(){
String sql = "select * from student";
/*
BeanPropertyRowMapper: 类
1. RowMapper接口的实现类
2. BeanPropertyRowMapper(xx.class); 返回值的泛型
*/
/*
* BeanPropertyRowMapper (底层反射),思路如下:
* 1. 实现RowMapper接口
* 2. 重写mapRow方法 : 每行ResultSet -> javaBean
* 1. 获取结果集中的数据
* 知道结果集有哪些字段 -> 结果集元数据
* 值 = resultSet.get(字段);
* id值 = id
*
* 2. 设置到javabean中去 (需要传参: Student.class)
* clazz = Student.class //获取类
* Student s = clazz.newInstance(); // javabean规范: 默认调用空参构造
* // Student s = new Student(); //等同于上面两行
*
* //并不知道Student对象有哪些方法,通过反射如下
* setIdMethod = clazz.getMethod("setId",int.class);
* //怎么知道Student对象中有setId方法呢?
* //因为javabean规范 : 必有set方法。额外要求:set+名字(必须要和表中的字段名一致)
* setIdMethod.invoke(s,id值);
*/
RowMapper<Student> rowMapper = new BeanPropertyRowMapper<>(Student.class);
List<Student> list = template.query(sql, rowMapper);
System.out.println(list); //打印出同query01()
}
}
4.源码和测试
package com.itheima03.source;
import com.itheima.utils.JdbcUtil;
import com.itheima01.template.Student;
import org.junit.Test;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/*
* 代码封装:
* 1. 先把完整的代码写出来
* 2. 进行抽取(定义方法, 参数, 返回值)
* 扩展性:
* 1. 不变的内容留在框架里
* 2. 变化的内容变成参数
*/
public class SrcDemo {
@Test
public void updateSrc() throws SQLException {
//如下完整代码写出来
String sql = "insert into student values(null,?,?)";
DataSource ds = JdbcUtil.getDs(); //连接池
Connection conn = ds.getConnection(); //拿到连接
PreparedStatement pstm = conn.prepareStatement(sql); //预编译
pstm.setString(1,"ww");
pstm.setString(2,"20");
int count = pstm.executeUpdate();
System.out.println(count);
}
@Test
public void update01() throws SQLException {
//测试MyTemplate.java中封装的update()。这里面代码和本文第一章update01()一样。
String sql = "insert into student values(null,?,?)";
MyTemplate template = new MyTemplate(JdbcUtil.getDs());
int count = template.update(sql, "ml", 33);
System.out.println(count);
}
@Test
public void update02() throws SQLException {
//测试MyTemplate.java中封装的update()
String sql = "insert into student values(null,?,?)";
MyTemplate template = new MyTemplate(JdbcUtil.getDs());
int count = template.update(sql); //没传参,不合法,所以需要框架封装者做健壮性校验
System.out.println(count);
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void query01() throws SQLException {
//如下完整代码写出来,query源码
String sql = "select * from student where id > ?";
DataSource ds = JdbcUtil.getDs();
Connection conn = ds.getConnection();
PreparedStatement pstm = conn.prepareStatement(sql);
pstm.setInt(1,2); //1指第一个?,2对?赋值
ResultSet resultSet = pstm.executeQuery();
// resultSet -> List<Student>
List<Student> list = new ArrayList<>();
while(resultSet.next()){
int id = resultSet.getInt("id");
int age = resultSet.getInt("age");
String name = resultSet.getString("name");
Student s = new Student();
s.setId(id);
s.setName(name);
s.setAge(age);
list.add(s);
}
System.out.println(list);
//[Student{id=3,name='ww',age=20},Student{id=4,name='ml',age=33}]
}
@Test
public void query02() throws SQLException {
//测试MyTemplate.java中封装的query()
String sql = "select * from student where id > ?";
MyTemplate02 template = new MyTemplate02(JdbcUtil.getDs());
MyRowMapper<Student> rm = new MyRowMapper() {
@Override
public Student mapRow(ResultSet resultSet) throws SQLException {
//父类方法抛出编译异常,子类才能抛。
int id = resultSet.getInt("id");
int age = resultSet.getInt("age");
String name = resultSet.getString("name");
Student s = new Student();
s.setId(id);
s.setName(name);
s.setAge(age);
return s;
}
};
List<Student> list = template.query(sql, rm, 2);
System.out.println(list);
//打印同上[Student{id=3,name='ww',age=20},Student{id=4,name='ml',age=33}]
}
}
如下右边rm是父类引用,rm.mapRow执行子类重写方法。左边resultSet已由右边遍历结束了。
5.封装
package com.itheima03.source;
import com.itheima.utils.JdbcUtil;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class MyTemplate {
//框架设计者即封装源码
DataSource ds; //不同的方法,同一个ds连接池
public MyTemplate(DataSource ds){
//通过构造传入ds
this.ds = ds;
}
/*
* 代码健壮性: 调用此方法传入的参数个数必须和sql的?数量一致
* 1. 参数的个数:args.length
* 2. sql的?的数量:如下a或b都可以
* a. 截取字符串
* b. 元数据 metadata !!!
* brand(元数据) = 香奈儿 (数据)
* 字段(元数据) = 对应的值(数据)
*/
public int update(String sql,Object... args) throws SQLException {
//封装updateSrc()
// String sql = "insert into student values(null,?,?)"; //这句会变化,用参数传入
// DataSource ds = JdbcUtil.getDs(); //连接池 //不要和JdbcUtil耦合,所以DataSource ds放到成员位置,通过构造传入。
// pstm.setString(1,"ww"); //也不知道sql占位多少,抽出去。换成下面 for (int i = 0; i < args.length; i++) {pstm.setObject(i+1,args[i]);} //都是从1往后排,没有0,(null,?,?)不是数组
Connection conn = ds.getConnection(); //拿到连接
PreparedStatement pstm = conn.prepareStatement(sql); //预编译
//获取参数元数据 (name=?,age=?) //下面5行健壮性校验,不让框架使用者乱用
ParameterMetaData parameterMetaData = pstm.getParameterMetaData();
int parameterCount = parameterMetaData.getParameterCount();
if(args.length != parameterCount){
throw new StupidBirdException("你个傻鸟,参数个数和?个数对应不上~~");
}
for (int i = 0; i < args.length; i++) {
pstm.setObject(i+1,args[i]);
}
int count = pstm.executeUpdate(); //这里要连接数据库报错,上面健壮性校验编译时就报错,不用连数据库
pstm.close();
conn.close();
return count;
}
public void execute(){
}
}
class StupidBirdException extends SQLException{
//自定义异常
public StupidBirdException(String msg){
super(msg);
}
}
如下是query方法封装
package com.itheima03.source;
import com.itheima.utils.JdbcUtil;
import com.itheima01.template.Student;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class MyTemplate02 {
DataSource ds;
public MyTemplate02(DataSource ds){
this.ds = ds;
}
public <T>List<T> query(String sql,MyRowMapper<T> rm,Object... args) throws SQLException {
//往参数列表设置接口方式,强迫用户重写抽象方法
// String sql = "select * from student where id > ?"; // 变量 -> 参数
// DataSource ds = JdbcUtil.getDs();
Connection conn = ds.getConnection();
PreparedStatement pstm = conn.prepareStatement(sql);
// pstm.setInt(1,2);
int parameterCount = pstm.getParameterMetaData().getParameterCount();
if(parameterCount != args.length){
//健壮性校验
throw new IllegalArgumentException("傻鸟~~");
}
for (int i = 0; i < args.length; i++) {
//快捷键 args.fori
pstm.setObject(i+1,args[i]);
}
ResultSet resultSet = pstm.executeQuery();
List<T> list = new ArrayList<>();
while(resultSet.next()){
//一次遍历一行,将这一行映射为java bean对象,所以叫mapRow
/*
* 如下代码会动态变化的(sql不同,结果集就会不同)。希望由用户自己来封装javaBean对象
* 1. 之前: 变量动态 -> 参数(全局,局部)
* 2. 现在: 如何将一段代码动态化:一段代码 提取成 方法
* (在java中,方法不能作为参数,参数只能是基本类型或引用类型[类,接口,枚举,注解])
*
* 如下我可以提供结果集给你,你要给我数据设置好的javaBean对象
* Student 方法名(resultSet); 封装没办法写方法体,需要用户自己写 -> 抽象方法
* 所以 方法名mapRow 要放在 接口MyRowMapper 里
*/
/* int id = resultSet.getInt("id");
int age = resultSet.getInt("age");
String name = resultSet.getString("name");
Student s = new Student();
s.setId(id);
s.setName(name);
s.setAge(age);*/
T s = rm.mapRow(resultSet); //给你resultSet,还我T类
list.add(s);
}
pstm.close();
conn.close();
return list;
}
}
interface MyRowMapper<T>{
T mapRow(ResultSet resultSet) throws SQLException;
}
B站/知乎/微信公众号:码农编程录