javaweb學習總結(四十)——編寫自己的JDBC框架


一、元數據介紹

  元數據指的是"數據庫"、"表"、"列"的定義信息。

1.1、DataBaseMetaData元數據

  Connection.getDatabaseMetaData()獲得代表DatabaseMetaData元數據的DatabaseMetaData對象。
  DataBaseMetaData對象的常用方法:

  • getURL():返回一個String類對象,代表數據庫的URL。
  • getUserName():返回連接當前數據庫管理系統的用戶名。
  • getDatabaseProductName():返回數據庫的產品名稱。
  • getDatabaseProductVersion():返回數據庫的版本號。
  • getDriverName():返回驅動驅動程序的名稱。
  • getDriverVersion():返回驅動程序的版本號。
  • isReadOnly():返回一個boolean值,指示數據庫是否只允許讀操作。
 1     /**
 2     * @Method: testDataBaseMetaData
 3     * @Description: 獲取數據庫的元信息
 4     * @Anthor:孤傲蒼狼
 5     *
 6     * @throws SQLException
 7     */ 
 8     @Test
 9     public void testDataBaseMetaData() throws SQLException {
10         Connection conn = JdbcUtils.getConnection();
11         DatabaseMetaData metadata = conn.getMetaData();
12         //getURL():返回一個String類對象,代表數據庫的URL
13         System.out.println(metadata.getURL());
14         //getUserName():返回連接當前數據庫管理系統的用戶名
15         System.out.println(metadata.getUserName());
16         //getDatabaseProductName():返回數據庫的產品名稱
17         System.out.println(metadata.getDatabaseProductName());
18         //getDatabaseProductVersion():返回數據庫的版本號
19         System.out.println(metadata.getDatabaseProductVersion());
20         //getDriverName():返回驅動驅動程序的名稱
21         System.out.println(metadata.getDriverName());
22         //getDriverVersion():返回驅動程序的版本號
23         System.out.println(metadata.getDriverVersion());
24         //isReadOnly():返回一個boolean值,指示數據庫是否只允許讀操作
25         System.out.println(metadata.isReadOnly());
26         JdbcUtils.release(conn, null, null);
27     }

  運行結果如下:

  

1.2、ParameterMetaData元數據

  PreparedStatement.getParameterMetaData() 獲得代表PreparedStatement元數據的ParameterMetaData對象。
  Select * from user where name=? And password=?
  ParameterMetaData對象的常用方法:

  • getParameterCount(): 獲得指定參數的個數
  • getParameterType(int param):獲得指定參數的sql類型,MySQL數據庫驅動不支持
 1     /**
 2     * @Method: testParameterMetaData
 3     * @Description: 獲取參數元信息
 4     * @Anthor:孤傲蒼狼
 5     *
 6     * @throws SQLException
 7     */ 
 8     @Test
 9     public void testParameterMetaData() throws SQLException {
10         Connection conn = JdbcUtils.getConnection();
11         String sql = "select * from user wherer name=? and password=?";
12         //將SQL預編譯一下
13         PreparedStatement st = conn.prepareStatement(sql);
14         ParameterMetaData pm = st.getParameterMetaData();
15         //getParameterCount() 獲得指定參數的個數
16         System.out.println(pm.getParameterCount());
17         //getParameterType(int param):獲得指定參數的sql類型,MySQL數據庫驅動不支持
18         System.out.println(pm.getParameterType(1));
19         JdbcUtils.release(conn, null, null);
20     }

1.3、ResultSetMetaData元數據

  ResultSet. getMetaData() 獲得代表ResultSet對象元數據的ResultSetMetaData對象。
  ResultSetMetaData對象的常用方法:

  • getColumnCount() 返回resultset對象的列數
  • getColumnName(int column) 獲得指定列的名稱
  • getColumnTypeName(int column)獲得指定列的類型
 1     /**
 2     * @Method: testResultSetMetaData
 3     * @Description: 結果集的元數據
 4     * @Anthor:孤傲蒼狼
 5     *
 6     * @throws Exception
 7     */
 8     @Test
 9     public void testResultSetMetaData() throws Exception {
10         Connection conn = JdbcUtils.getConnection();
11         String sql = "select * from account";
12         PreparedStatement st  = conn.prepareStatement(sql);
13         ResultSet rs = st.executeQuery();
14         //ResultSet.getMetaData()獲得代表ResultSet對象元數據的ResultSetMetaData對象
15         ResultSetMetaData metadata = rs.getMetaData();
16         //getColumnCount() 返回resultset對象的列數
17         System.out.println(metadata.getColumnCount());
18         //getColumnName(int column) 獲得指定列的名稱
19         System.out.println(metadata.getColumnName(1));
20         //getColumnTypeName(int column)獲得指定列的類型
21         System.out.println(metadata.getColumnTypeName(1));
22         JdbcUtils.release(conn, st, rs);
23     }

二、使用元數據封裝簡單的JDBC框架

  系統中所有實體對象都涉及到基本的CRUD操作
  所有實體的CUD操作代碼基本相同,僅僅發送給數據庫的SQL語句不同而已,因此可以把CUD操作的所有相同代碼抽取到工具類的一個update方法中,並定義參數接收變化的SQL語句。
  實體的R操作,除SQL語句不同之外,根據操作的實體不同,對ResultSet的映射也各不相同,因此可義一個query方法,除以參數形式接收變化的SQL語句外,可以使用策略模式由qurey方法的調用者決定如何把ResultSet中的數據映射到實體對象中。

2.1、封裝通用的update方法和qurey方法

  定義一個JdbcUtils工具類,工具類負責獲取數據庫連接,釋放資源,執行SQL的update和query操作,代碼如下:

  1 package me.gacl.util;
  2 
  3 import java.io.InputStream;
  4 import java.sql.Connection;
  5 import java.sql.DriverManager;
  6 import java.sql.PreparedStatement;
  7 import java.sql.ResultSet;
  8 import java.sql.SQLException;
  9 import java.sql.Statement;
 10 import java.util.Properties;
 11 
 12 public class JdbcUtils {
 13 
 14     private static String driver = null;
 15     private static String url = null;
 16     private static String username = null;
 17     private static String password = null;
 18     
 19     static{
 20         try{
 21             //讀取db.properties文件中的數據庫連接信息
 22             InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
 23             Properties prop = new Properties();
 24             prop.load(in);
 25             
 26             //獲取數據庫連接驅動
 27             driver = prop.getProperty("driver");
 28             //獲取數據庫連接URL地址
 29             url = prop.getProperty("url");
 30             //獲取數據庫連接用戶名
 31             username = prop.getProperty("username");
 32             //獲取數據庫連接密碼
 33             password = prop.getProperty("password");
 34             
 35             //加載數據庫驅動
 36             Class.forName(driver);
 37             
 38         }catch (Exception e) {
 39             throw new ExceptionInInitializerError(e);
 40         }
 41     }
 42     
 43     /**
 44     * @Method: getConnection
 45     * @Description: 獲取數據庫連接對象
 46     * @Anthor:孤傲蒼狼
 47     *
 48     * @return Connection數據庫連接對象
 49     * @throws SQLException
 50     */ 
 51     public static Connection getConnection() throws SQLException{
 52         return DriverManager.getConnection(url, username,password);
 53     }
 54     
 55     /**
 56     * @Method: release
 57     * @Description: 釋放資源,
 58     *     要釋放的資源包括Connection數據庫連接對象,負責執行SQL命令的Statement對象,存儲查詢結果的ResultSet對象
 59     * @Anthor:孤傲蒼狼
 60     *
 61     * @param conn
 62     * @param st
 63     * @param rs
 64     */ 
 65     public static void release(Connection conn,Statement st,ResultSet rs){
 66         if(rs!=null){
 67             try{
 68                 //關閉存儲查詢結果的ResultSet對象
 69                 rs.close();
 70             }catch (Exception e) {
 71                 e.printStackTrace();
 72             }
 73             rs = null;
 74         }
 75         if(st!=null){
 76             try{
 77                 //關閉負責執行SQL命令的Statement對象
 78                 st.close();
 79             }catch (Exception e) {
 80                 e.printStackTrace();
 81             }
 82         }
 83         
 84         if(conn!=null){
 85             try{
 86                 //關閉Connection數據庫連接對象
 87                 conn.close();
 88             }catch (Exception e) {
 89                 e.printStackTrace();
 90             }
 91         }
 92     }
 93     
 94     /**
 95     * @Method: update
 96     * @Description: 萬能更新
 97     * 所有實體的CUD操作代碼基本相同,僅僅發送給數據庫的SQL語句不同而已,
 98     * 因此可以把CUD操作的所有相同代碼抽取到工具類的一個update方法中,並定義參數接收變化的SQL語句
 99     * @Anthor:孤傲蒼狼
100     * @param sql 要執行的SQL
101     * @param params 執行SQL時使用的參數
102     * @throws SQLException
103     */ 
104     public static void update(String sql,Object params[]) throws SQLException{
105         Connection conn = null;
106         PreparedStatement st = null;
107         ResultSet rs = null;
108         try{
109             conn = getConnection();
110             st = conn.prepareStatement(sql);
111             for(int i=0;i<params.length;i++){
112                 st.setObject(i+1, params[i]);
113             }
114             st.executeUpdate();
115             
116         }finally{
117             release(conn, st, rs);
118         }
119     }
120     
121     /**
122     * @Method: query
123     * @Description:萬能查詢
124     * 實體的R操作,除SQL語句不同之外,根據操作的實體不同,對ResultSet的映射也各不相同,
125     * 因此可義一個query方法,除以參數形式接收變化的SQL語句外,可以使用策略模式由qurey方法的調用者決定如何把ResultSet中的數據映射到實體對象中。
126     * @Anthor:孤傲蒼狼
127     *
128     * @param sql 要執行的SQL
129     * @param params 執行SQL時使用的參數
130     * @param rsh 查詢返回的結果集處理器
131     * @return
132     * @throws SQLException
133     */ 
134     public static Object query(String sql,Object params[],ResultSetHandler rsh) throws SQLException{
135         
136         Connection conn = null;
137         PreparedStatement st = null;
138         ResultSet rs = null;
139         
140         try{
141             conn = getConnection();
142             st = conn.prepareStatement(sql);
143             for(int i=0;i<params.length;i++){
144                 st.setObject(i+1, params[i]);
145             }
146             rs = st.executeQuery();
147             /**
148              * 對於查詢返回的結果集處理使用到了策略模式,
149              * 在設計query方法時,query方法事先是無法知道用戶對返回的查詢結果集如何進行處理的,即不知道結果集的處理策略,
150              * 那么這個結果集的處理策略就讓用戶自己提供,query方法內部就調用用戶提交的結果集處理策略進行處理
151              * 為了能夠讓用戶提供結果集的處理策略,需要對用戶暴露出一個結果集處理接口ResultSetHandler
152              * 用戶只要實現了ResultSetHandler接口,那么query方法內部就知道用戶要如何處理結果集了
153              */
154             return rsh.handler(rs);
155             
156         }finally{
157             release(conn, st, rs);
158         }
159     }
160 }

  在設計query方法時,對於查詢返回的結果集處理使用到了策略模式,query方法事先是無法知道用戶對返回的查詢結果集如何進行處理的,即不知道結果集的處理策略, 那么這個結果集的處理策略就讓用戶自己提供,query方法內部就調用用戶提交的結果集處理策略進行處理, 為了能夠讓用戶提供結果集的處理策略,需要對用戶暴露出一個結果集處理接口ResultSetHandler, 結果集處理器接口ResultSetHandler的定義如下:

 1 package me.gacl.util;
 2 
 3 import java.sql.ResultSet;
 4 
 5 /**
 6 * @ClassName: ResultSetHandler
 7 * @Description:結果集處理器接口
 8 * @author: 孤傲蒼狼
 9 * @date: 2014-10-5 下午12:01:27
10 *
11 */ 
12 public interface ResultSetHandler {
13     
14     /**
15     * @Method: handler
16     * @Description: 結果集處理方法
17     * @Anthor:孤傲蒼狼
18     *
19     * @param rs 查詢結果集
20     * @return
21     */ 
22     public Object handler(ResultSet rs);
23 }

  用戶只要實現了ResultSetHandler接口,那么就是針對查詢結果集寫了一個處理器,在query方法內部就調用用戶自己寫的處理器處理結果集。

2.2、編寫常用的結果集處理器

  為了提高框架的易用性,我們可以事先就針對結果集寫好一些常用的處理器,比如將結果集轉換成bean對象的處理器,將結果集轉換成bean對象的list集合的處理器。

2.2.1、BeanHandler——將結果集轉換成bean對象的處理器

 1 package me.gacl.util;
 2 
 3 import java.lang.reflect.Field;
 4 import java.sql.ResultSet;
 5 import java.sql.ResultSetMetaData;
 6 
 7 /**
 8 * @ClassName: BeanHandler
 9 * @Description: 將結果集轉換成bean對象的處理器
10 * @author: 孤傲蒼狼
11 * @date: 2014-10-5 下午12:00:33
12 *
13 */ 
14 public class BeanHandler implements ResultSetHandler {
15     private Class<?> clazz;
16     public BeanHandler(Class<?> clazz){
17         this.clazz = clazz;
18     }
19     public Object handler(ResultSet rs) {
20         try{
21             if(!rs.next()){
22                 return null;
23             }
24             Object bean = clazz.newInstance();
25             //得到結果集元數據
26             ResultSetMetaData metadata = rs.getMetaData();
27             int columnCount = metadata.getColumnCount();//得到結果集中有幾列數據
28             for(int i=0;i<columnCount;i++){
29                 String coulmnName = metadata.getColumnName(i+1);//得到每列的列名
30                 Object coulmnData = rs.getObject(i+1);
31                 Field f = clazz.getDeclaredField(coulmnName);//反射出類上列名對應的屬性
32                 f.setAccessible(true);
33                 f.set(bean, coulmnData);
34             }
35             return bean;
36         }catch (Exception e) {
37             throw new RuntimeException(e);
38         }
39     }
40 }

2.2.2、BeanListHandler——將結果集轉換成bean對象的list集合的處理器

 1 package me.gacl.util;
 2 
 3 import java.lang.reflect.Field;
 4 import java.sql.ResultSet;
 5 import java.sql.ResultSetMetaData;
 6 import java.util.ArrayList;
 7 import java.util.List;
 8 
 9 /**
10 * @ClassName: BeanListHandler
11 * @Description: 將結果集轉換成bean對象的list集合的處理器
12 * @author: 孤傲蒼狼
13 * @date: 2014-10-5 下午12:00:06
14 *
15 */ 
16 public class BeanListHandler implements ResultSetHandler {
17     private Class<?> clazz;
18     public BeanListHandler(Class<?> clazz){
19         this.clazz = clazz;
20     }
21     
22     public Object handler(ResultSet rs) {
23         try{
24             List<Object> list = new ArrayList<Object>();
25             while(rs.next()){
26                 Object bean = clazz.newInstance();
27                 
28                 ResultSetMetaData  metadata = rs.getMetaData();
29                 int count = metadata.getColumnCount();
30                 for(int i=0;i<count;i++){
31                     String name = metadata.getColumnName(i+1);
32                     Object value = rs.getObject(name);
33                     
34                     Field f = bean.getClass().getDeclaredField(name);
35                     f.setAccessible(true);
36                     f.set(bean, value);
37                 }
38                 list.add(bean);
39             }
40             return list.size()>0?list:null;
41         
42         }catch (Exception e) {
43             throw new RuntimeException(e);
44         }
45     }
46 }

  當框架自身提供的結果集處理器不滿足用戶的要求時,那么用戶就可以自己去實現ResultSetHandler接口,編寫滿足自己業務要求的結果集處理器。

  有了上述的JdbcUtils框架之后,針對單個實體對象CRUD操作就非常方便了,如下所示:

 1 package me.gacl.dao;
 2 
 3 import java.sql.SQLException;
 4 import java.util.List;
 5 import me.gacl.domain.Account;
 6 import me.gacl.util.BeanHandler;
 7 import me.gacl.util.BeanListHandler;
 8 import me.gacl.util.JdbcUtils;
 9 
10 public class AccountDao {
11 
12     public void add(Account account) throws SQLException{
13         String sql = "insert into account(name,money) values(?,?)";
14         Object params[] = {account.getName(),account.getMoney()};
15         JdbcUtils.update(sql, params);
16     }
17     
18     
19     public void delete(int id) throws SQLException{
20         String sql = "delete from account where id=?";
21         Object params[] = {id};
22         JdbcUtils.update(sql, params);
23     }
24     
25     public void update(Account account) throws SQLException{
26         
27         String sql = "update account set name=?,money=? where id=?";
28         Object params[] = {account.getName(),account.getMoney(),account.getId()};
29         JdbcUtils.update(sql, params);
30         
31     }
32     
33     public Account find(int id) throws SQLException{
34         String sql = "select * from account where id=?";
35         Object params[] = {id};
36         return (Account) JdbcUtils.query(sql, params, new BeanHandler(Account.class));
37     }
38     
39     public List<Account> getAll() throws SQLException{
40         String sql = "select * from account";
41         Object params[] = {};
42         return (List<Account>) JdbcUtils.query(sql, params,new BeanListHandler(Account.class));
43     }
44 }

   編寫的這個JDBC框架就是模擬Apache的DBUtils框架的實現,下一篇將具體介紹Apache的DBUtils框架。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM