1. 程式人生 > >JDBC06-----程式碼重構之封裝DBCUtils工具類

JDBC06-----程式碼重構之封裝DBCUtils工具類

一. 程式碼重構原則

之前的DAO操作,除了sql語句和set值不一樣,其它都一樣,有太多的重複程式碼。因此需要重構。

重構程式碼原則:

二. 抽取DML方法

1. 具體抽取成executeUpdate方法在每一個dao實現中

 1 /**
 2      * 重構DML操作
 3      */
 4     public int executeUpdate(String sql,Object...params) {
 5         Connection connection=null;
 6         PreparedStatement pStatement=null
; 7 8 try { 9 //1.載入驅動 10 //2.連線資料庫 11 connection=JDBCUtil.getConnection(); 12 //3.建立語句 13 pStatement=connection.prepareStatement(sql); 14 //4.遍歷引數 15 for(int i=0;i<params.length;i++) { 16 pStatement.setObject(i+1, params[i]);
17 } 18 //4.執行語句 19 return pStatement.executeUpdate(); 20 } catch (SQLException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 }finally { 24 JDBCUtil.close(connection, pStatement, null); 25 }
26 return 0; 27 } 28 29 public void save(Stu stu) { 30 String sql="insert into stu(name,age) values(?,?)"; 31 this.executeUpdate(sql,stu.getName(),stu.getAge()); 32 }
View Code

測試:

1 @Test
2     public void testSave() {
3         IStuDao dao=new StuDaoImpl();
4         Stu stu=new Stu();
5         stu.setName("hehe");
6         stu.setAge(23);
7         dao.save(stu);
8     }
View Code

這樣可以成功,但是倘若每個dao的實現類裡面都寫這個executeUpdate方法,還是有寫多餘,因此可以抽取到一個類中。如果放在JDBCUtil類中,也不好,因為JDBCUtil是直接和資料互動的,因此可以新建立一個類CRUDTemplate。

2. 抽取到CRUDTemplate中----最終抽取地方

 1 public class CRUDTemplate {
 2     /**
 3      * 重構DML操作
 4      */
 5     public static int executeUpdate(String sql,Object...params) {
 6         Connection connection=null;
 7         PreparedStatement pStatement=null;
 8         
 9         try {
10             //1.載入驅動
11             //2.連線資料庫
12             connection=JDBCUtil.getConnection();
13             //3.建立語句
14             pStatement=connection.prepareStatement(sql);
15             //4.遍歷引數
16             for(int i=0;i<params.length;i++) {
17                 pStatement.setObject(i+1, params[i]);
18             }
19             //4.執行語句
20             return pStatement.executeUpdate();
21         } catch (SQLException e) {
22             // TODO Auto-generated catch block
23             e.printStackTrace();
24         }finally {
25             JDBCUtil.close(connection, pStatement, null);
26         }
27         return 0;
28     }
29 
30 }
View Code
1 public void save(Stu stu) {
2         String sql="insert into stu(name,age) values(?,?)";
3         CRUDTemplate.executeUpdate(sql,stu.getName(),stu.getAge());
4     }
View Code

三. DQL抽取

1. DQL抽取

 1 /**
 2      * DQL抽取
 3      */
 4     public static List<Stu> executeQuery(String sql,Object...params){
 5         Connection connection=null;
 6         PreparedStatement pStatement=null;
 7         ResultSet resultSet=null;
 8         //建立一個集合
 9         List<Stu> list=new ArrayList<Stu>();
10         try {
11             //1.載入驅動
12             //2.連線資料庫
13             connection=JDBCUtil.getConnection();
14             //3.建立語句
15             pStatement=connection.prepareStatement(sql);
16             //遍歷引數
17             for(int i=0;i<params.length;i++) {
18                 pStatement.setObject(i+1, params[i]);
19             }
20             //4.執行語句
21             resultSet=pStatement.executeQuery();
22             while(resultSet.next()) {
23                 Stu stu=new Stu();
24                 stu.setName(resultSet.getString("name"));
25                 stu.setAge(resultSet.getInt("age"));
26                 stu.setId(resultSet.getInt("id"));
27                 list.add(stu);
28             }
29             return list;
30         }catch (Exception e) {
31             e.printStackTrace();
32         }finally {
33             //5.釋放資源
34             JDBCUtil.close(connection, pStatement, resultSet);
35         }
36         return null;
37     }
View Code
 1 public Stu get(int id) {
 2         String sql="select *from stu where id=?";
 3         List<Stu> list=CRUDTemplate.executeQuery(sql, id);
 4         return list.size()==1?list.get(0):null;
 5     }
 6     
 7 
 8     public List<Stu> getAll() {
 9         String sql="select *from stu";
10         return CRUDTemplate.executeQuery(sql);
11     }
View Code
 1 @Test
 2     public void testGet() {
 3         IStuDao dao=new StuDaoImpl();
 4         Stu student=dao.get(2);
 5         System.out.println(student);
 6         System.out.println(student.getName());
 7     }
 8 
 9     @Test
10     public void testGetAll() {
11         IStuDao dao=new StuDaoImpl();
12         List<Stu> list=dao.getAll();
13         System.out.println(list.toArray());
14     }
View Code

2. 存在問題

上面的DQL抽取雖然簡單了,但是還存在一個問題,那就是在while迴圈中,只操作了Stu這個表格,寫死了,但是,當表格很多,不僅僅想讓它適用於Stu表,還想使用更多的表,就需要額外處理。因此,表的型別處理應該放在executeQuery方法之外來執行。

四. 結果集處理程式碼實現

1. 定義一個結果集處理介面

1 package com.test.jdbctest.handler;
2 
3 import java.sql.ResultSet;
4 import java.util.List;
5 
6 public interface IResultSetHandler {
7     List handle(ResultSet rs);
8 
9 }
View Code

2. 在dao實現類中新增新的結果處理介面實現類

 1 class StuResultSetHandImp implements IResultSetHandler{
 2 
 3     public List handle(ResultSet rs) {
 4         List<Stu> list=new ArrayList<Stu>();
 5         try {
 6             while(rs.next()) {
 7                 Stu stu=new Stu();
 8                 stu.setName(rs.getString("name"));
 9                 stu.setAge(rs.getInt("age"));
10                 stu.setId(rs.getInt("id"));
11                 list.add(stu);
12             }
13         } catch (SQLException e) {
14             // TODO Auto-generated catch block
15             e.printStackTrace();
16         }
17         return list;
18     }
19 
20 
21 
22 放在daoimpl類裡面的末尾處
View Code

3. 修改template

 1 /**
 2      * DQL抽取
 3      */
 4     public static List<Stu> executeQuery(String sql,IResultSetHandler handler,Object...params){
 5         Connection connection=null;
 6         PreparedStatement pStatement=null;
 7         ResultSet resultSet=null;
 8         //建立一個集合
 9         List list=new ArrayList();
10         try {
11             //1.載入驅動
12             //2.連線資料庫
13             connection=JDBCUtil.getConnection();
14             //3.建立語句
15             pStatement=connection.prepareStatement(sql);
16             //4.執行語句
17             resultSet=pStatement.executeQuery();
18             return handler.handle(resultSet);
19         }catch (Exception e) {
20             e.printStackTrace();
21         }finally {
22             //5.釋放資源
23             JDBCUtil.close(connection, pStatement, resultSet);
24         }
25         return null;
26     }
View Code

4. 使用泛型

上面的結果集都是返回的list,但是有時候我們可能不僅僅是返回物件,有可能返回的是int的數量,比如查詢共有多少行。因此,需要把返回型別改寫成泛型,到時候呼叫的時候再決定返回什麼型別。

(1)修改結果處理集介面

public interface IResultSetHandler<T> {
    T handle(ResultSet rs);

}

(2)修改實現

class StuResultSetHandImp implements IResultSetHandler<List<Stu>>{

    public List<Stu> handle(ResultSet rs) {
        List<Stu> list=new ArrayList<Stu>();
        try {
            while(rs.next()) {
                Stu stu=new Stu();
                stu.setName(rs.getString("name"));
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getInt("id"));
                list.add(stu);
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return list;
    }
    

(3)修改template

public static <T>T executeQuery(String sql,IResultSetHandler<T> handler,Object...params){
        Connection connection=null;
        PreparedStatement pStatement=null;
        ResultSet resultSet=null;
        try {
            //1.載入驅動
            //2.連線資料庫
            connection=JDBCUtil.getConnection();
            //3.建立語句
            pStatement=connection.prepareStatement(sql);
            for(int i=0;i<params.length;i++) {
                pStatement.setObject(i+1, params[i]);
            }
            //4.執行語句
            resultSet=pStatement.executeQuery();
            return handler.handle(resultSet);
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            //5.釋放資源
            JDBCUtil.close(connection, pStatement, resultSet);
        }
        return null;
    }

5. 存在問題

發現其實每個表格查詢做的事情差不多,如果每一個表都建立一個handler,是不好的。因此,可以統一處理:

使用內省可以解決上面的問題。

五. 內省

 1. 內省的簡單使用

假設有表stu,有id,name,age三個屬性,且有對應的domain類

現在做如下操作:

 1 class ClassTest{
 2     public Class class1;
 3     public ClassTest(Class class1) {
 4         this.class1=class1;
 5     }
 6 }
 7 public class Test {
 8     public static void main(String args[]) throws Exception {
 9         Stu stu=Stu.class.newInstance();
10         //獲取指定位元組碼的屬性資訊
11         BeanInfo beanInfo=Introspector.getBeanInfo(Stu.class,Object.class);
12         //獲取所有的屬性描述器
13         PropertyDescriptor pds[]=beanInfo.getPropertyDescriptors();
14         for(PropertyDescriptor pd:pds) {
15             System.out.println(pd.getName());
16             System.out.println(pd.getReadMethod());
17             System.out.println(pd.getWriteMethod());
18         }
19         PropertyDescriptor propertyDescriptor=new PropertyDescriptor("name", Stu.class);
20         System.out.println(propertyDescriptor.getName());
21         propertyDescriptor.getWriteMethod().invoke(stu, "zhangsan");
22         System.out.println(stu.getName());
23     }
24 
25 }
View Code
 1 age
 2 public java.lang.Integer com.test.jdbctest.domain.Stu.getAge()
 3 public void com.test.jdbctest.domain.Stu.setAge(java.lang.Integer)
 4 id
 5 public java.lang.Integer com.test.jdbctest.domain.Stu.getId()
 6 public void com.test.jdbctest.domain.Stu.setId(java.lang.Integer)
 7 name
 8 public java.lang.String com.test.jdbctest.domain.Stu.getName()
 9 public void com.test.jdbctest.domain.Stu.setName(java.lang.String)
10 name
11 zhangsan
View Code

2. 內省應用於結果處理集

1)建立一個BeanHandler

修改dao實現

2)新建立BeanListHandler

 

因此,上面每個類一個handerl就改成了BeanHanler和BeanListHandler了

六. DBUtils

之前幾個章節都是介紹的如何一步步實現我們自己的JDBC重構,且可以把我們自己寫的轉換成jar包,以便自己專案中使用。實際上,有很多第三方很好用的JDBC工具,DBUtils就是其中的一款,它為我們提供了呼叫介面,我們直接使用就好。但是,底層的原理其實和我們之前學習的重構思想也有相似之處。可以接下來研究下。

 

 

 

 

 

參考文獻

https://ke.qq.com/course/339214