实现简易JDBC框架

1 准备

  •  JDBC 基本知识
  •    JDBC元数据知识
  •  反射基本知识

2:  两个问题

 

  • 业务背景:系统中全部实体对象都涉及到基本的CRUD操做。全部实体的CUD操做代码基本相同,仅仅是发送给数据库的sql语句不一样而已,所以能够把CUD操做的全部相同代码抽取到工具类的一个update方法中,并定义参数接收变化的sql语句。java

  • 实体的R操做,除sql语句不一样以外,根据操做的实体不一样,对ResultSet的映射也各不相同,所以可定义一个query方法,除以参数形式接收变化的sql语句外,能够使用策略模式由query方法的调用者决定如何把ResultSet中的数据映射到实体对象中。mysql

3: JDBC 封装 update query方法

  

public class JdbcNewUtils {
    private JdbcNewUtils() {}
    /**
     * 这里能够使用properties进行替换
     */
    private static final String USER = "root";
    private static final String PWD = "root";
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/day?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&generateSimpleParameterMetadata=true";
    private static final String DRIVER= "com.mysql.jdbc.Driver";
    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    public static Connection  getConnection() throws SQLException {
        Connection connection = DriverManager.getConnection(URL, USER, PWD);
        return connection;
    }
    /**
     *  CUD 返回影响数目
     * @param sql
     * @param args
     * @return int
     */
    public static int  update(String sql,Object [] args) {
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn=getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 1; i <= args.length; i++) {
                ps.setObject(i, args[i-1]);
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(conn, ps);
        }
        return 0;
    }
    /**
      *  查询结果封装Bean
     * @param sql
     * @param args
     * @param rsh
     * @return Object
     */
    public static Object  query(String sql,Object [] args,ResultSetHandler  rsh) {
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn=getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1, args[i]);
            }
            return rsh.handle(ps.executeQuery());
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(conn, ps);
        }
        return null;
    }
    
    /**
    * 关闭全部打开的资源
    */
    public static void close(Connection conn, Statement stmt) {
        if(stmt!=null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    /**
    * 关闭全部打开的资源
    */
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        if(rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        close(conn, stmt);
    }

}

4: query

 每次查询根据查询的参数不一样, 返回的ResultSet 也不一样, 这个规则咱们须要单独编写规则解析器, 这里用到了策略设计模式,sql

将ResultSetHandler 定义解决问题的接口, handle为那些须要实现的具体解决的办法数据库

public interface ResultSetHandler {
    Object handle(ResultSet resultSet);
}

 

 下面我实现了Beanhandler 和 BeanListHandler 分别是 单个的Bean 和一个列表的Bean设计模式

package jdbc.simpleframwork;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

public class BeanHandler implements ResultSetHandler {

    private Class<?> obj;
    public BeanHandler(Class<?> obj) {
        this.obj = obj;
    }
    @Override
    public Object handle(ResultSet resultSet){
        try {
            if(!resultSet.next()) {
                return null;
            }
            Object instance = obj.newInstance();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int count = metaData.getColumnCount();
            for(int i=1;i<=count;i++) {
                Field f = obj.getDeclaredField(metaData.getColumnName(i));
                f.setAccessible(true);
                f.set(instance, resultSet.getObject(i));
            }
            return instance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }

}
package jdbc.simpleframwork;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;

public class BeanListHandler implements ResultSetHandler {

    private Class<?> clazz;

    public BeanListHandler(Class<?> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public Object handle(ResultSet resultSet) {
        try {
            ArrayList<Object> objlist = new ArrayList<>();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int count = metaData.getColumnCount();
            while (resultSet.next()) {
                Object instace = clazz.newInstance();
                for (int i = 0; i < count; i++) {
                    Field f = clazz.getDeclaredField(metaData.getColumnName(i + 1));
                    f.setAccessible(true);
                    f.set(instace, resultSet.getObject(i + 1));
                    f.setAccessible(false);
                }
                objlist.add(instace);

            }
            return objlist;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

5 测试

public class TestFramwork {

    public static void main(String[] args) throws SQLException {
        Connection conn = JdbcNewUtils.getConnection();

        String sql = "select * from student where id=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        Student stu = (Student) JdbcNewUtils.query(sql, new Object[] { 1 }, new BeanHandler(Student.class));
        System.out.println(stu);

        String sql2 = "select * from student";
        ArrayList<Student> list = (ArrayList<Student>) JdbcNewUtils.query(sql2, new Object[] {},
                new BeanListHandler(Student.class));
        System.out.println(list);
    }

}

 

6: 总结:

Update系列操做:框架

  •   对于CUD操做,SQL只有站位符号的多少发生了改变,对于传递参数才是咱们须要关注的地方,可是JDBC提供了一系列传递参数解析的办法,经过set系列函数,将参数值传递进行,因此咱们只须要封装一个通用的update便可

Query系列操做ide

  •   对R操做,就复杂得多,SQL语句的不一样,返回的ResultSet也不一样,能够单个Bean 或者一个List,一个Map等,能够看出来,实际上不少框架提供的也就是这些方法的封装

 

对了 真正应用上 咱们的DAO 一边是 下面的写法函数

public class AccountDao {
    public void add(Account account) throws SQLException{
        String sql = "insert into account(name , money) values(?, ?)";
        Object[] params = {account.getName(), account.getMoney()};
        JdbcUtils.update(sql, params);
    }
    
    public void delete(int id ) throws SQLException{
        String sql = "delete from account where id = ?";
        Object[] params = {id};
        JdbcUtils.update(sql, params);
    }
    
    public void update(Account account) throws SQLException{
        String sql = "update account set name = ?, money = ? where id = ?";
        Object params[] = {account.getName(), account.getMoney(), account.getId()};
        JdbcUtils.update(sql, params);
    }
    
    public Account find(int id ) throws SQLException{
        String sql = "select * from account where id = ?";
        Object params[] = {id};
        return (Account) JdbcUtils.query(sql, params, new BeanHandler(Account.class));
    }
    
    public List getAll() throws SQLException{
        String sql = "select * from account";
        Object params[] = {};
        return (List)JdbcUtils.query(sql, params, new BeanListHandler(Account.class));
    }
}