3-自定义JDBC框架

自定义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类,提供一些成员变量
    Studeng类的成员变量

    表的字段信息

    注意: 成员变量的数据类型和名称要和表中的列保持一致
  • 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);
    }
}
测试结果
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容