實現簡易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));
    }
}
相關文章
相關標籤/搜索