自定义JDBC框架

  • 使用JDBC过程中有大量的重复代码, 核心功能仅仅执行一条SQL语句, 所以可以抽取一个JDBC模板类, 来封装一些方法(update query).
  • 专门帮我们执行增删改查SQL语句. 将之前那些重复的操作, 都抽取到模板类中的方法里, 就能大简化使用步骤

源信息

  • DataeBaseMetaData: 数据库的源信息
  • java.sql.DataBaseMetaData 封装了整个数据库的综合信息
    • 例如 String getDatabaseProductName(): 获取数据库产品的名称
    • 例如int getDatabaseProductVersion: 获取数据库产品的版本号
  • ParameterMetaData: 参数源信息
    • java.sql.ParameterMetaData 封装的是预编译执行者对象中每个参数的类型和属性, 这个对象可以通过预编译执行者对象中的getParameterMetaData()方法来获取
    • int getParameterCount() 用于获取SQL语句中的参数个数
  • ResultSetMetaData: 结果集的源信息
    • java.sql.ResultSetMetaData: 封装的是结果集对象中列的类型和属性, 这个对象可以通过结果集对象中的getMetaData()方法来获取
    • int getColumnCount() 用于获取列的总数, String getColumnName(int i)用于获取列名
package com.test005;

import com.lizicai.utils.DataSourceUtils;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.*;

public class JDBCTemplate {
    // 1. 定义参数变量(数据源, 连接对象, 执行者对象, 结果集对象
    private DataSource dataSource;
    private Connection con;
    private PreparedStatement pst;
    private ResultSet rs;

//    2. 通过有参构造为数据源赋值
    public JDBCTemplate(DataSource dataSource){
        this.dataSource = dataSource;
    }

//    3. 定义update方法, 参数 sql语句 sql语句中的参数

    public int update(String sql, Object... objs){
        int result = 0;

        try {
            con = dataSource.getConnection();

            pst = con.prepareStatement(sql);

            ParameterMetaData parameterMetaData = pst.getParameterMetaData();

            int count = parameterMetaData.getParameterCount();

            if( count != objs.length){
                throw new RuntimeException("参数个数不匹配");
            }

            for(int i=0; i< objs.length;i++){
                pst.setObject(i+1,objs[i]);
            }

            result = pst.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DataSourceUtils.close(con, pst);
        }

        return  result;
    }
}
package com.test005;

import com.lizicai.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 = {5,"周七",27,"1997-07-07"};
        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 name=?";
        Object[] params = {37,"周七"};
        int result = template.update(sql, params);
        System.out.println(result);
    }

    @Test
    public void delete(){
        String sql = "DELETE FROM student where name=?";
        Object[] params = {"周七"};
        int result = template.update(sql, params);
        System.out.println(result);
    }
}

查询功能-框架编写

  • 查询一条记录并封装对象的方法: queryForObject()

  • 查询多条记录并封装集合的方法: queryForList()

  • 查询聚合函数并返回单条数据的方法: queryForScalar()

  • 实体类的编写

    • 定义一个类, 提供一些成员变量( 成员变量的数据类型和表中的列保持一致)
package com.test005.domain;

import java.util.Date;

public class Student {
    private Integer sid;
    private String name;
    private Integer age;
    private Date birthday;
    // get set constructor 略
}
package com.test005.handler;

import java.sql.ResultSet;

public interface ResultSetHandler<T> {
    <T> T handler(ResultSet rs);
}
package com.test005.handler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

// 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);
                    Method writeMethod = pd.getWriteMethod();
                    writeMethod.invoke(bean, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 14. 返回封装好的对象
        return bean;
    }
}
 public <T> T queryForObject(String sql, ResultSetHandler<T> rsh, Object...objs){
        T obj = null;

        try {
            con = dataSource.getConnection();

            pst = con.prepareStatement(sql);

            ParameterMetaData parameterMetaData = pst.getParameterMetaData();

            int count = parameterMetaData.getParameterCount();

            if( count != objs.length){
                throw new RuntimeException("参数个数不匹配");
            }

            for(int i=0; i< objs.length;i++){
                pst.setObject(i+1,objs[i]);
            }

            rs = pst.executeQuery();

            obj = rsh.handler(rs);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DataSourceUtils.close(con, pst);
        }

        return  obj;
    }
@Test
public void queryForObject(){
    String sql = "SELECT * FROM student WHERE sid=?";
    Student stu = template.queryForObject(sql, new BeanHandler<>(Student.class), 1);
    System.out.println(stu);
}

查询列表

package com.test005.handler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

// 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> beanList = 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);
                    Method writeMethod = pd.getWriteMethod();
                    writeMethod.invoke(bean, value);
                }
                beanList.add(bean);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 14. 返回封装好的对象
        return beanList;
    }
}
/**
 * 查询多条记录
 */
public <T> List<T> queryListForObject(String sql, ResultSetHandler<T> rsh, Object...objs){
    List<T> list = new ArrayList<>();

    try {
        con = dataSource.getConnection();

        pst = con.prepareStatement(sql);

        ParameterMetaData parameterMetaData = pst.getParameterMetaData();

        int count = parameterMetaData.getParameterCount();

        if( count != objs.length){
            throw new RuntimeException("参数个数不匹配");
        }

        for(int i=0; i< objs.length;i++){
            pst.setObject(i+1,objs[i]);
        }

        rs = pst.executeQuery();

        list = rsh.handler(rs);

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        DataSourceUtils.close(con, pst);
    }

    return  list;
}
@Test
public void queryForList(){
    String sql = "SELECT * FROM student";
    List<Student> list = template.queryListForObject(sql, new BeanListHandler<>(Student.class));
    for(Student stu : list){
        System.out.println(stu);
    }
}

查询聚合函数

package com.test005.handler;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
 * 1. 定义一个类, 实现ResultSetHandler接口
 * 2. 我一定handler方法
 * 3. 定义一个Long类型变量
 * 4. 判断结果集对象中是否还有数据
 * 5. 获取结果集源信息的对象
 * 6. 获取第一列的列名
 * 7. 根据列名获取该列的值
 * 8. 返回结果
 */
// * 1. 定义一个类, 实现ResultSetHandler接口
public class ScalarHandler <T> implements ResultSetHandler<T>{

    //2. 我一定handler方法
    @Override
    public Long handler(ResultSet rs) {
        // 3. 定义一个Long类型变量
        Long value = null;

        try {
            // 4. 判断结果集对象中是否还有数据
            if(rs.next()){
                // 5. 获取结果集源信息的对象
                ResultSetMetaData metaData = rs.getMetaData();

                //6. 获取第一列的列名
                String columnName = metaData.getColumnName(1);
                // 7. 根据列名获取该列的值
                value = rs.getLong(columnName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }
}
/**
 *  聚合聚合函数的查询结果
 */
public Long queryForScalar(String sql, ResultSetHandler<Long> rsh, Object...objs){
    Long value = null;

    try {
        con = dataSource.getConnection();

        pst = con.prepareStatement(sql);

        ParameterMetaData parameterMetaData = pst.getParameterMetaData();

        int count = parameterMetaData.getParameterCount();

        if( count != objs.length){
            throw new RuntimeException("参数个数不匹配");
        }

        for(int i=0; i< objs.length;i++){
            pst.setObject(i+1,objs[i]);
        }

        rs = pst.executeQuery();

        value = rsh.handler(rs);

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        DataSourceUtils.close(con, pst);
    }

    return  value;
}
@Test
public void queryForScalar(){
    String sql = "SELECT COUNT(*) FROM student";

    Long value = template.queryForScalar(sql, new ScalarHandler<Long>());
    System.out.println(value);
}