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

你可能感兴趣的:(javaweb)