抽取JDBCTemplate

yesye發表於2021-09-09

抽取JDBCTemplate

為了解決DAO實現類中程式碼的重複問題,另外使得程式碼更通用,所以抽取一個公共的模板,使其更具有通用性。

DAO實現類的程式碼

public class StudentDAOImpl implements IStudentDAO {    public void save(Student stu) {
        String sql = "INSERT INTO s_student(name,age) VALUES(?,?)";
        Connection conn = null;
        PreparedStatement ps = null;        try {            //載入註冊驅動,獲取連線物件
            conn = JdbcUtil.getConn();
            ps = conn.prepareStatement(sql);
            ps.setString(1, stu.getName());
            ps.setInt(2, stu.getAge());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{            //釋放資源
            JdbcUtil.close(conn, ps, null);
        }
    }    public void delete(Long id) {
        String sql = "DELETE FROM s_student WHERE id = ?";
        Connection conn = null;
        PreparedStatement ps = null;        try {            //載入註冊驅動
            //獲取連線物件
            conn = JdbcUtil.getConn();            //獲取語句物件
            ps = conn.prepareStatement(sql);
            ps.setLong(1, id);            //執行sql語句
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{            //釋放資源
            JdbcUtil.close(conn, ps, null);
        }
    }    public void update(Student newStu) {
        String sql = "UPDATE s_student SET name = ?,age = ? WHERE id = ?;";
        Connection conn = null;
        PreparedStatement ps = null;        try {            //載入註冊驅動
            //獲取連線物件
            conn = JdbcUtil.getConn();            //獲取語句物件
            ps = conn.prepareStatement(sql);
            ps.setString(1, newStu.getName());
            ps.setInt(2, newStu.getAge());
            ps.setLong(3, newStu.getId());            //執行sql語句
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{            //釋放資源
            JdbcUtil.close(conn, ps, null);
        }
    }    public Student get(Long id) {
        String sql = "SELECT * FROM s_student WHERE id = ? "; 
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;        try {            //載入註冊驅動
            //獲取連線物件
            conn = JdbcUtil.getConn();            // 獲取語句物件
            ps = conn.prepareStatement(sql);
            ps.setLong(1, id);            // 執行SQL語句
            rs = ps.executeQuery();            if (rs.next()) {
                Student stu = new Student();
                Long sid = rs.getLong("id");
                String name = rs.getString("name");
                Integer age = rs.getInt("age");

                stu.setId(sid);
                stu.setName(name);
                stu.setAge(age);                return stu;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, rs);
        }        return null;
    }    public List<Student> list() {
        List<Student> list = new ArrayList<>();
        String sql = "SELECT * FROM s_student";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;        try {            //載入註冊驅動
            //獲取連線物件
            conn = JdbcUtil.getConn();            //獲取語句物件
            ps = conn.prepareStatement(sql);            //執行sql語句
            rs = ps.executeQuery(sql);            //查詢操作
            while(rs.next()){
                Student stu = new Student();
                Long id = rs.getLong("id");
                String name = rs.getString("name");
                Integer age = rs.getInt("age");
                
                stu.setName(name);
                stu.setId(id);
                stu.setAge(age);
                
                list.add(stu);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            JdbcUtil.close(conn, ps, rs);
        }        return list;
    }
}

發現save,delete,update方法的程式碼只有sql不同,所以,把公共的程式碼提出來放在模板類中的update方法中,把sql作為引數傳到方法中。因為存在sql語句中的佔位符並且站位符的個數並不確定,可以採用可變引數進行傳參,把需要給站位符設定的站放在一個Object陣列中,作為引數傳到update方法中,在update方法中又把陣列中的值迭代出來賦給特定的sql語句

public void save(Student stu) {
        String sql = "INSERT INTO  s_student(name,age) VALUES(?,?)";
        JdbcTemplate.update(sql,stu.getName(),stu.getAge());
    }    public void delete(Long id) {
        String sql = "DELETE FROM s_student WHERE id = ?";
        JdbcTemplate.update(sql, id);
    }    
    public void update(Student newStu) {
        String sql = "UPDATE s_student SET name = ?,age = ? WHERE id = ?;";
        JdbcTemplate.update(sql, newStu.getName(),newStu.getAge(),newStu.getId());
    }

模板中的update方法:

    /**
     * DML語句的操作的模板
     * @param sql  sql語句 :UPDATE INSERT DELETE
     * @param params sql語句中的站位符? 對應值的陣列
     * @return      返回受影響的行數
     */public static int update(String sql,Object...params){
    Connection conn = null;
    PreparedStatement ps = null;    try {        //載入註冊驅動,獲取連線物件
        conn = JdbcUtil.getConn();
        ps = conn.prepareStatement(sql);        //
        for(int i = 0; i < params.length; i++){
            ps.setObject(i + 1, params[i]);
        }        return ps.executeUpdate();
    } catch (Exception e) {
        e.printStackTrace();
    }finally{        //釋放資源
        JdbcUtil.close(conn, ps, null);
    }    return 0;
}

query的抽取有些麻煩,因為它會返回一個結果集,處理結果集的行為,不應該作為模板中的程式碼,而是應該交給給自的DAO來完成,因為給自的DAO才知道各自表的列有哪一些,作為模板肯定只有在模板中呼叫DAO中處理完成後的結果集,但是為了保證傳入query方法的引數是一致的,肯定需要定義一個規範,在程式中也就是定義一個介面,把這個介面就叫做結果集處理器,每一個DAO的實現類中要有一個處理結果的內部類,這個內部類去實現結果處理器介面,返回一個結果集供模板呼叫. 也就是,模板中query方法表面呼叫介面中的方法,實際呼叫的是各個DAO實現類中的結果集.這就是多型思想

//結果集處理器public interface IResultSetHandler<T> {    T handle(ResultSet rs) throws Exception;
}

學生結果集處理器 實現了結果處理器介面

public class StudentHandler implements IResultSetHandler<List<Student>>{    @Override
    public List<Student> handle(ResultSet rs) throws Exception {

        List<Student> list = new ArrayList<>();        while(rs.next()){
            Student stu = new Student();
            stu.setAge(rs.getInt("age"));
            stu.setId(rs.getLong("id"));
            stu.setName(rs.getString("name"));

            list.add(stu);
        }        return list;
    }
}

DAO實現類中的get方法和list方法變成了:

public Student get(Long id) {
    String sql = "SELECT * FROM s_student WHERE id = ? "; 
    List<Student> list = JdbcTemplate.query(sql,new StudentHandler(), id);    return list.size() == 1?list.get(0) : null;
}public List<Student> list() {
    String sql = "SELECT * FROM s_student";    return JdbcTemplate.query(sql,new StudentHandler());
}

模板中的qurey方法

//DQL語句的操作模板public static <T> T query(String sql,IResultSetHandler<T> ih,Object...params){
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;    try {
        conn = JdbcUtil.getConn();        //獲取語句物件
        ps = conn.prepareStatement(sql);        for(int i = 0; i < params.length; i++){
            ps.setObject(i + 1, params[i]);
        }        //執行sql語句
        rs = ps.executeQuery();        //處理結果集
        return ih.handle(rs);

    } catch (Exception e) {
        e.printStackTrace();
    }finally{
        JdbcUtil.close(conn, ps, rs);
    }    return null;
}

上述qurey方法抽取還存在一個問題就是,每個DAO實現類都得實現介面編寫各自的結果處理器,如果DAO比較多,這個也就很麻煩了,能不能抽取值抽取一個更加通用的呢,那就得滿足一定的規範,比如可以透過內省機制可以完成,但是列名必須和JavaBean的屬性名相同。

public class BeanHandler<T> implements IResultSetHandler<T>{    
    private Class<T> classType = null;    public BeanHandler(Class<T> classType){        this.classType = classType;
    }    @Override
    public T handle(ResultSet rs) throws Exception {        //建立對應類的物件
        T obj = classType.newInstance();        if(rs.next()){
            BeanInfo info = Introspector.getBeanInfo(classType,Object.class);
            PropertyDescriptor[] pds = info.getPropertyDescriptors();            for (PropertyDescriptor ps : pds) {
                String column = ps.getName();
                Object val = rs.getObject(column);
                ps.getWriteMethod().invoke(obj,val);
            }
        }        return obj;
    }
}
public class BeanListHandler<T> implements IResultSetHandler<List<T>>{    private Class<T> classType = null;    public BeanListHandler(Class<T> classType){        this.classType = classType;
    }    @Override
    public List<T> handle(ResultSet rs) throws Exception {
        List<T> list = new ArrayList<>();        //建立物件
        //獲取物件描述器
        while(rs.next()){
            T obj = classType.newInstance();
            BeanInfo info = Introspector.getBeanInfo(classType,Object.class);
            PropertyDescriptor[] pds = info.getPropertyDescriptors();            for (PropertyDescriptor ps : pds) {                //獲取物件的屬性名,屬性名和列名相同就呼叫setter方法把某一列的資料設定到物件中
                String columnName = ps.getName();
                Object val = rs.getObject(columnName);
                ps.getWriteMethod().invoke(obj, val);
            }
            list.add(obj);
        }        return list;
    }

}

這時候的DAO實現類中方法可以這樣來:

public Student get(Long id) {
    String sql = "SELECT * FROM s_student WHERE id = ? "; 
    return JdbcTemplate.query(sql,new BeanHandler<>(Student.class), id);
}public List<Student> list() {
    String sql = "SELECT * FROM s_student";    return JdbcTemplate.query(sql, new BeanListHandler<>(Student.class));
}

原文出處:https://www.cnblogs.com/tfper/p/9959031.html  

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2041/viewspace-2817648/,如需轉載,請註明出處,否則將追究法律責任。

相關文章