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     }

  运行结果如下:

  javaweb学习总结(四十)——编写自己的JDBC框架详解编程语言

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框架。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/11373.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论