自定义JDBC框架
定义必要的信息、获取数据库的连接、释放资源都是重复的代码,在操作JDBC时通常都是执行SQL语句就可以了,所以需要抽取出来一个模板类来封装一些方法(Update、Query),专门执行增删改查的SQL语句,简化使用步骤。
源信息
1.DataBaseMetaData: 数据库的源信息(了解就行)
java.sql.DataBaseMetaData: 封装整个数据库的综合信息
- String getDatabaseProductName():获取数据库产品的名称
- int getDatabaseProductVersion():获取数据库产品的版本号
2.ParameterMetaData: 参数的源信息
java.sql.ParameterMetaData 封装的是预编译执行者对象中每个参数的类型和属性,这个对象可以通过预编译执行者对象中的getParameterMetaData()方法来获取
- 核心功能: int getParameterCount()用于获取sql语句中参数的个数
3.ResultSetMetaData: 结果集的源信息
java.sql.ResultSetMetaData: 封装的是结果集对象中列的类型和属性,这个对象可以通过结果及对象中的getMetaData()方法来获取
- 核心功能: int getColumnCount()用于获取列的总数
- 核心功能: String getColumnName(int i) 用于获取列名
框架编写
- 用于执行增删改功能的update()方法
1.定义所需要成员变量(数据源,连接对象,执行者对象,结果集对象)
2.定义有参构造,为数据源对象赋值
3.定义update()方法,参数:sql语句,sql语句所需参数
4.定义int类型变量,用于接收sql语句执行后影响的行数
5.通过数据源获取一个数据库连接
6.通过数据库连接对象获取执行者对象并对sql语句进行预编译
7.通过执行这对象获取sql语句中参数的源信息对象
8.通过源信息对象获取sql语句中参数的个数
9.判断参数个数是否一致
10.为sql语句中? 占位符赋值
11.执行sql语句并接收结果
12.释放资源
13.返回结果
package com.itheima06;
/*
JDBCTemplate类增删改功能的编写
*/
public class JDBCTemplate {
//1.定义所需要成员变量(数据源,数据库连接对象,执行者对象,结果集对象)
private DataSource dataSource;
private Connection con;
private ResultSet rs;
private PreparedStatement pst;
//2.定义有参构造,为数据源对象赋值
public JDBCTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
//3.定义update()方法,参数:sql语句,sql语句所需参数
public int update(String sql, Object... objs) {
//4.定义int类型变量,用于接收sql语句执行后影响的行数
int result = 0;
try {
//5.通过数据源获取一个数据库连接
con = dataSource.getConnection();
//6.通过数据库连接对象获取执行者对象并对sql语句进行预编译
pst = con.prepareStatement(sql);
/*
注意这里拿到了执行者对象不能想着直接去执行sql语句,
如果传进来的参数和占位符不等,就会有异常,所以要判断参数和占位符相不相等!
*/
//7.通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//8.通过源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//9.判断是否一致
if (count != objs.length) {
throw new RuntimeException("参数不匹配!");
}
//10.为sql语句占位符赋值
for (int i = 0; i < objs.length; i++) {
pst.setObject(i+1,objs[i]);
}
//11.执行sql语句,并接受结果
result = pst.executeUpdate();
} catch (Exception E) {
E.printStackTrace();
}finally {
//12. 释放资源
DataSourceUtils.close(con,pst);
}
//13.返回结果
return result;
}
}
- update()方法的测试
1.定义测试类,模拟dao层
2.测试执行insert语句
3.测试执行update语句
4.测试执行delete语句
package com.itheima06;
import com.itheima.utils.DataSourceUtils;
import org.junit.Test;
public class JDBCTemplateTest1 {
//这里之前编写数据库连接池的工具类,从工具类里获取数据源对象
private JDBCTemplate template = new JDBCTemplate(DataSourceUtils.getDataSource());
@Test
public void insert() {
//新增数据的测试
String sql = "insert into student values(?,?,?,?)";
Object[] params = {6, "老八", 28, "1998-08-08"};
int result = template.update(sql, params);
if (result != 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
}
@Test
public void update() {
//修改数据的测试
String sql = "update student set age=? where id=?";
Object[] params = {88, 5};
int result = template.update(sql, params);
System.out.println(result);
}
@Test
public void delete() {
//删除数据
String sql = "delete from student where id=?";
Object[] params = {5};
int result = template.update(sql, params);
System.out.println(result);
}
}
- 用于查询功能的方法介绍
1.查询一条记录并封装对象的方法: queryForObject()
2.查询多条记录并封装集合的方法: queryForList()
3.查询集合函数并返回单条数据的方法: queryForScalar()
1.查询一条记录并封装对象的方法: queryForObject()
- 1.定义一个Student类,提供一些成员变量
注意: 成员变量的数据类型和名称要和表中的列保持一致- 2.处理结果集的接口
定义泛型为ResultSetHandler<T>
定义用于处理结果集的泛型方法<T> T handler(ResultSet rs)
注意: 此接口仅用于为不同处理结果集的方式提供预览,具体的实现类还需要自行编写
- 接口类编写
package com.itheima07.handler;
import java.sql.ResultSet;
/*
用于处理结果集的接口
*/
public interface ResultSetHandler<T> {
<T> T handler(ResultSet rs);
}
- 实现类编写
/*
用于处理结果集的接口的实现类
*/
package com.itheima07.handler;
//1.定义一个类,实现ResultSetHandler接口
public class BeanHandler<T> implements ResultSetHandler<T> {
//2.定义Class对象类型变量
private Class<T> beanClass;
//3.通过有参构造为变量赋值
public BeanHandler(Class<T> beanClass) {
this.beanClass = beanClass;
}
//4.重写handler方法。用于将一条记录封装到自定义对象中
@Override
public T handler(ResultSet rs) {
//5.声明自定义对象类型
T bean = null;
try {
//6.创建传递参数的对象,为自定义对象赋值
bean = beanClass.newInstance();
//7.判断结果集中是否有数据
if (rs.next()){
//8.通过结果集对象获取结果集源信息的对象
ResultSetMetaData metaData = rs.getMetaData();
//9.通过结果集源信息对象获取列数
int count = metaData.getColumnCount();
//10.通过循环遍历列数
for (int i = 1; i <= count; i++) {
//11.通过结果集源信息对象获取列名
String columnName = metaData.getColumnName(i);
//12.通过列名获取该列的数据
Object value = rs.getObject(columnName);
//13.创建属性描述器对象,将获取到的值通过该对象的set方法进行赋值
PropertyDescriptor pd= new PropertyDescriptor(columnName.toLowerCase(),beanClass);
//获取set方法
Method writeMethod = pd.getWriteMethod();
//执行set方法,给成员变量赋值
writeMethod.invoke(bean,value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
//14.返回封装好的对象
return bean;
}
}
- 框架类编写
package com.itheima07;
/*
JDBCTemplate类查询功能的编写
*/
public class JDBCTemplate {
//1.定义所需要成员变量(数据源,数据库连接对象,执行者对象,结果集对象)
private DataSource dataSource;
private Connection con;
private ResultSet rs;
private PreparedStatement pst;
//2.定义有参构造,为数据源对象赋值
public JDBCTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
/*
查询方法: 用于将一条记录封装成自定义对象并返回
*/
public <T> T queryForObject(String sql, ResultSetHandler<T> rsh, Object... objs) {
T obj = null;
try {
//5.通过数据源获取一个数据库连接
con = dataSource.getConnection();
//6.通过数据库连接对象获取执行者对象并对sql语句进行预编译
pst = con.prepareStatement(sql);
/*
注意这里拿到了执行者对象不能想着直接去执行sql语句,
如果传进来的参数和占位符不等,就会有异常,所以要判断参数和占位符相不相等!
*/
//7.通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//8.通过源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//9.判断是否一致
if (count != objs.length) {
throw new RuntimeException("参数不匹配!");
}
//10.为sql语句占位符赋值
for (int i = 0; i < objs.length; i++) {
pst.setObject(i + 1, objs[i]);
}
//11.执行sql语句,并接收结果
rs = pst.executeQuery();
//12.通过BeanHandler方式对结果集进行处理
obj = rsh.handler(rs);
} catch (Exception E) {
E.printStackTrace();
} finally {
//12. 释放资源
DataSourceUtils.close(con, pst);
}
//13.返回结果
return obj;
}
}
- 测试类
package com.itheima07;
/*
测试结果
*/
public class JDBCTemplateTest1 {
//这里之前编写数据库连接池的工具类,从工具类里获取数据源对象
private JDBCTemplate template = new JDBCTemplate(DataSourceUtils.getDataSource());
@Test
public void queryForObject() {
//新增数据的测试
String sql = "SELECT * FROM student WHERE id=?";
Student stu = template.queryForObject(sql, new BeanHandler<>(Student.class), 1);
System.out.println(stu);
}
}
2.查询多条记录并封装集合的方法: queryForList()
- 实现类
package com.itheima07.handler;
/*
实现类2: 用于查询到的多条记录,封装到Student对象并添加到集合返回
*/
//1.定义一个类,实现ResultSetHandler接口
public class BeanListHandler<T> implements ResultSetHandler<T> {
//2.定义Class对象类型变量
private Class<T> beanClass;
//3.通过有参构造为变量赋值
public BeanListHandler(Class<T> beanClass) {
this.beanClass = beanClass;
}
//4.重写handler方法。用于将对条记录封装到自定义对象中并添加集合返回
@Override
public List<T> handler(ResultSet rs) {
//5.声明自定义对象类型
List<T> list = new ArrayList<>();
try {
//6.判断结果集中是否有数据
while (rs.next()){
//7.创建传递参数的对象,为自定义对象赋值
T bean = beanClass.newInstance();
//8.通过结果集对象获取结果集源信息的对象
ResultSetMetaData metaData = rs.getMetaData();
//9.通过结果集源信息对象获取列数
int count = metaData.getColumnCount();
//10.通过循环遍历列数
for (int i = 1; i <= count; i++) {
//11.通过结果集源信息对象获取列名
String columnName = metaData.getColumnName(i);
//12.通过列名获取该列的数据
Object value = rs.getObject(columnName);
//13.创建属性描述器对象,将获取到的值通过该对象的set方法进行赋值
PropertyDescriptor pd= new PropertyDescriptor(columnName.toLowerCase(),beanClass);
//获取set方法
Method writeMethod = pd.getWriteMethod();
//执行set方法,给成员变量赋值
writeMethod.invoke(bean,value);
}
//14.将对象添加到集合当中
list.add(bean);
}
} catch (Exception e) {
e.printStackTrace();
}
//14.返回封装好的对象
return list;
}
}
- 框架类
package com.itheima07;
/*
JDBCTemplate类查询功能的编写
*/
public class JDBCTemplate {
//1.定义所需要成员变量(数据源,数据库连接对象,执行者对象,结果集对象)
private DataSource dataSource;
private Connection con;
private ResultSet rs;
private PreparedStatement pst;
//2.定义有参构造,为数据源对象赋值
public JDBCTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
public <T> List<T> queryForList(String sql, ResultSetHandler<T> rsh, Object... objs) {
List<T> list = new ArrayList<>();
try {
//5.通过数据源获取一个数据库连接
con = dataSource.getConnection();
//6.通过数据库连接对象获取执行者对象并对sql语句进行预编译
pst = con.prepareStatement(sql);
/*
注意这里拿到了执行者对象不能想着直接去执行sql语句,
如果传进来的参数和占位符不等,就会有异常,所以要判断参数和占位符相不相等!
*/
//7.通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//8.通过源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//9.判断是否一致
if (count != objs.length) {
throw new RuntimeException("参数不匹配!");
}
//10.为sql语句占位符赋值
for (int i = 0; i < objs.length; i++) {
pst.setObject(i + 1, objs[i]);
}
//11.执行sql语句,并接收结果
rs = pst.executeQuery();
//12.通过BeanListHandler方式对结果集进行处理
list = rsh.handler(rs);
} catch (Exception E) {
E.printStackTrace();
} finally {
//12. 释放资源
DataSourceUtils.close(con, pst);
}
//13.返回结果
return list;
}
}
- 测试类
/*
测试结果
*/
public class JDBCTemplateTest1 {
//这里之前编写数据库连接池的工具类,从工具类里获取数据源对象
private JDBCTemplate template = new JDBCTemplate(DataSourceUtils.getDataSource());
@Test
public void queryForList() {
String sql = "SELECT * FROM student";
List<Student> list = template.queryForList(sql, new BeanListHandler<>(Student.class));
for (Student student : list) {
System.out.println(student);
}
}
}
3.查询集合函数并返回单条数据的方法: queryForScalar()
- 实现类编写
package com.itheima07.handler;
/*
实现类:查询聚合函数的SQL语句
*/
//定义一个类,实现ResultSetHandler接口
public class ScalarHandler<T> implements ResultSetHandler<T>{
//重写handler方法
@Override
public Long handler(ResultSet rs) {
//定义一个long类型变量
Long value = null;
try {
//判断结果集对象当中还有数据
if (rs.next()){
//获取结果集源信息的对象
ResultSetMetaData metaData = rs.getMetaData();
//获取第一列的列名
String columnName = metaData.getColumnName(1);
//跟据列名获取值
value = rs.getLong(columnName);
}
} catch (Exception e) {
e.printStackTrace();
}
//返回结果
return value;
}
}
- 框架类编写
package com.itheima07;
/*
JDBCTemplate类查询功能的编写
*/
public class JDBCTemplate {
//1.定义所需要成员变量(数据源,数据库连接对象,执行者对象,结果集对象)
private DataSource dataSource;
private Connection con;
private ResultSet rs;
private PreparedStatement pst;
//2.定义有参构造,为数据源对象赋值
public JDBCTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
/*
用于将聚合函数的查询结果返回
*/
public Long queryForScalar(String sql, ResultSetHandler<Long> rsh, Object... objs) {
Long value = null;
try {
//5.通过数据源获取一个数据库连接
con = dataSource.getConnection();
//6.通过数据库连接对象获取执行者对象并对sql语句进行预编译
pst = con.prepareStatement(sql);
/*
注意这里拿到了执行者对象不能想着直接去执行sql语句,
如果传进来的参数和占位符不等,就会有异常,所以要判断参数和占位符相不相等!
*/
//7.通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//8.通过源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//9.判断是否一致
if (count != objs.length) {
throw new RuntimeException("参数不匹配!");
}
//10.为sql语句占位符赋值
for (int i = 0; i < objs.length; i++) {
pst.setObject(i + 1, objs[i]);
}
//11.执行sql语句,并接收结果
rs = pst.executeQuery();
//12.通过ScalarHandler方式对结果集进行处理
value = rsh.handler(rs);
} catch (Exception E) {
E.printStackTrace();
} finally {
//12. 释放资源
DataSourceUtils.close(con, pst);
}
//13.返回结果
return value;
}
- 测试类编写
package com.itheima07;
import com.itheima.utils.DataSourceUtils;
import com.itheima07.domain.Student;
import com.itheima07.handler.BeanHandler;
import com.itheima07.handler.BeanListHandler;
import com.itheima07.handler.ScalarHandler;
import org.junit.Test;
import java.util.List;
/*
测试结果
*/
public class JDBCTemplateTest1 {
//这里之前编写数据库连接池的工具类,从工具类里获取数据源对象
private JDBCTemplate template = new JDBCTemplate(DataSourceUtils.getDataSource());
@Test
public void queryForScalar(){
String sql = "SELECT COUNT(*) FROM student";
Long value = template.queryForScalar(sql, new ScalarHandler<>());
System.out.println(value);
}
}