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=nullView Code; 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 }
測試:
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 zhangsanView Code
2. 內省應用於結果處理集
(1)建立一個BeanHandler
修改dao實現
(2)新建立BeanListHandler
因此,上面每個類一個handerl就改成了BeanHanler和BeanListHandler了
六. DBUtils
之前幾個章節都是介紹的如何一步步實現我們自己的JDBC重構,且可以把我們自己寫的轉換成jar包,以便自己專案中使用。實際上,有很多第三方很好用的JDBC工具,DBUtils就是其中的一款,它為我們提供了呼叫介面,我們直接使用就好。但是,底層的原理其實和我們之前學習的重構思想也有相似之處。可以接下來研究下。
參考文獻
https://ke.qq.com/course/339214