首页 > 代码库 > javaweb学习总结(四十)——编写自己的JDBC框架

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 =http://www.mamicode.com/ conn.getMetaData();12         //getURL():返回一个String类对象,代表数据库的URL13         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 =http://www.mamicode.com/ 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 要执行的SQL101     * @param params 执行SQL时使用的参数102     * @throws SQLException103     */ 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: query123     * @Description:万能查询124     * 实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,125     * 因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。126     * @Anthor:孤傲苍狼127     *128     * @param sql 要执行的SQL129     * @param params 执行SQL时使用的参数130     * @param rsh 查询返回的结果集处理器131     * @return132     * @throws SQLException133     */ 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              * 为了能够让用户提供结果集的处理策略,需要对用户暴露出一个结果集处理接口ResultSetHandler152              * 用户只要实现了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:2710 *11 */ 12 public interface ResultSetHandler {13     14     /**15     * @Method: handler16     * @Description: 结果集处理方法17     * @Anthor:孤傲苍狼18     *19     * @param rs 查询结果集20     * @return21     */ 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:3312 *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 =http://www.mamicode.com/ 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 = http://www.mamicode.com/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: BeanListHandler11 * @Description: 将结果集转换成bean对象的list集合的处理器12 * @author: 孤傲苍狼13 * @date: 2014-10-5 下午12:00:0614 *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 =http://www.mamicode.com/ 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 =http://www.mamicode.com/ 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框架。

javaweb学习总结(四十)——编写自己的JDBC框架