深入分析JavaWeb Item33 -- 开发自己简易的JDBC框架

JavaWeb 同时被 2 个专栏收录
54 篇文章 0 订阅
53 篇文章 56 订阅

一、元数据介绍

  元数据指的是”数据库“、“表“、”“的定义信息。

1.1、DataBaseMetaData元数据

  Connection.getDatabaseMetaData()获得代表DatabaseMetaData元数据的DatabaseMetaData对象。
  DataBaseMetaData对象的常用方法:

  • getURL():返回一个String类对象,代表数据库的URL。
  • getUserName():返回连接当前数据库管理系统的用户名。
  • getDatabaseProductName():返回数据库的产品名称。
  • getDatabaseProductVersion():返回数据库的版本号。
  • getDriverName():返回驱动驱动程序的名称。
  • getDriverVersion():返回驱动程序的版本号。
  • isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
/**
    * @Method: testDataBaseMetaData
    * @Description: 获取数据库的元信息
    * @Anthor:孤傲苍狼
    *
    * @throws SQLException
    */ 
    @Test
    public void testDataBaseMetaData() throws SQLException {
        Connection conn = JdbcUtils.getConnection();
        DatabaseMetaData metadata = conn.getMetaData();
        //getURL():返回一个String类对象,代表数据库的URL
        System.out.println(metadata.getURL());
        //getUserName():返回连接当前数据库管理系统的用户名
        System.out.println(metadata.getUserName());
        //getDatabaseProductName():返回数据库的产品名称
        System.out.println(metadata.getDatabaseProductName());
        //getDatabaseProductVersion():返回数据库的版本号
        System.out.println(metadata.getDatabaseProductVersion());
        //getDriverName():返回驱动驱动程序的名称
        System.out.println(metadata.getDriverName());
        //getDriverVersion():返回驱动程序的版本号
        System.out.println(metadata.getDriverVersion());
        //isReadOnly():返回一个boolean值,指示数据库是否只允许读操作
        System.out.println(metadata.isReadOnly());
        JdbcUtils.release(conn, null, null);
    }

  运行结果如下:

  这里写图片描述

1.2、ParameterMetaData元数据

  PreparedStatement.getParameterMetaData() 获得代表PreparedStatement元数据的ParameterMetaData对象。
  Select * from user where name=? And password=?
  ParameterMetaData对象的常用方法:

  • getParameterCount(): 获得指定参数的个数
  • getParameterType(int param):获得指定参数的sql类型,MySQL数据库驱动不支持
/**
    * @Method: testParameterMetaData
    * @Description: 获取参数元信息
    * @Anthor:孤傲苍狼
    *
    * @throws SQLException
    */ 
    @Test
    public void testParameterMetaData() throws SQLException {
        Connection conn = JdbcUtils.getConnection();
        String sql = "select * from user wherer name=? and password=?";
        //将SQL预编译一下
        PreparedStatement st = conn.prepareStatement(sql);
        ParameterMetaData pm = st.getParameterMetaData();
        //getParameterCount() 获得指定参数的个数
        System.out.println(pm.getParameterCount());
        //getParameterType(int param):获得指定参数的sql类型,MySQL数据库驱动不支持
        System.out.println(pm.getParameterType(1));
        JdbcUtils.release(conn, null, null);
    }

1.3、ResultSetMetaData元数据

  ResultSet. getMetaData() 获得代表ResultSet对象元数据的ResultSetMetaData对象。
  ResultSetMetaData对象的常用方法:

  • getColumnCount() 返回resultset对象的列数
  • getColumnName(int column) 获得指定列的名称
  • getColumnTypeName(int column)获得指定列的类型
/**
    * @Method: testResultSetMetaData
    * @Description: 结果集的元数据
    * @Anthor:孤傲苍狼
    *
    * @throws Exception
    */
    @Test
    public void testResultSetMetaData() throws Exception {
        Connection conn = JdbcUtils.getConnection();
        String sql = "select * from account";
        PreparedStatement st  = conn.prepareStatement(sql);
        ResultSet rs = st.executeQuery();
        //ResultSet.getMetaData()获得代表ResultSet对象元数据的ResultSetMetaData对象
        ResultSetMetaData metadata = rs.getMetaData();
        //getColumnCount() 返回resultset对象的列数
        System.out.println(metadata.getColumnCount());
        //getColumnName(int column) 获得指定列的名称
        System.out.println(metadata.getColumnName(1));
        //getColumnTypeName(int column)获得指定列的类型
        System.out.println(metadata.getColumnTypeName(1));
        JdbcUtils.release(conn, st, rs);
    }

二、使用元数据封装简单的JDBC框架

  系统中所有实体对象都涉及到基本的CRUD操作
  所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句。
  实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。

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

  定义一个JdbcUtils工具类,工具类负责获取数据库连接,释放资源,执行SQL的update和query操作,代码如下:

package me.gacl.util;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JdbcUtils {

    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static{
        try{
            //读取db.properties文件中的数据库连接信息
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties prop = new Properties();
            prop.load(in);

            //获取数据库连接驱动
            driver = prop.getProperty("driver");
            //获取数据库连接URL地址
            url = prop.getProperty("url");
            //获取数据库连接用户名
            username = prop.getProperty("username");
            //获取数据库连接密码
            password = prop.getProperty("password");

            //加载数据库驱动
            Class.forName(driver);

        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
    * @Method: getConnection
    * @Description: 获取数据库连接对象
    * @Anthor:孤傲苍狼
    *
    * @return Connection数据库连接对象
    * @throws SQLException
    */ 
    public static Connection getConnection() throws SQLException{
        return DriverManager.getConnection(url, username,password);
    }

    /**
    * @Method: release
    * @Description: 释放资源,
    *     要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
    * @Anthor:孤傲苍狼
    *
    * @param conn
    * @param st
    * @param rs
    */ 
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                //关闭存储查询结果的ResultSet对象
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                //关闭负责执行SQL命令的Statement对象
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try{
                //关闭Connection数据库连接对象
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
    * @Method: update
    * @Description: 万能更新
    * 所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,
    * 因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句
    * @Anthor:孤傲苍狼
    * @param sql 要执行的SQL
    * @param params 执行SQL时使用的参数
    * @throws SQLException
    */ 
    public static void update(String sql,Object params[]) throws SQLException{
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            conn = getConnection();
            st = conn.prepareStatement(sql);
            for(int i=0;i<params.length;i++){
                st.setObject(i+1, params[i]);
            }
            st.executeUpdate();

        }finally{
            release(conn, st, rs);
        }
    }

    /**
    * @Method: query
    * @Description:万能查询
    * 实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,
    * 因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
    * @Anthor:孤傲苍狼
    *
    * @param sql 要执行的SQL
    * @param params 执行SQL时使用的参数
    * @param rsh 查询返回的结果集处理器
    * @return
    * @throws SQLException
    */ 
    public static Object query(String sql,Object params[],ResultSetHandler rsh) throws SQLException{

        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try{
            conn = getConnection();
            st = conn.prepareStatement(sql);
            for(int i=0;i<params.length;i++){
                st.setObject(i+1, params[i]);
            }
            rs = st.executeQuery();
            /**
             * 对于查询返回的结果集处理使用到了策略模式,
             * 在设计query方法时,query方法事先是无法知道用户对返回的查询结果集如何进行处理的,即不知道结果集的处理策略,
             * 那么这个结果集的处理策略就让用户自己提供,query方法内部就调用用户提交的结果集处理策略进行处理
             * 为了能够让用户提供结果集的处理策略,需要对用户暴露出一个结果集处理接口ResultSetHandler
             * 用户只要实现了ResultSetHandler接口,那么query方法内部就知道用户要如何处理结果集了
             */
            return rsh.handler(rs);

        }finally{
            release(conn, st, rs);
        }
    }
}

  在设计query方法时,对于查询返回的结果集处理使用到了策略模式,query方法事先是无法知道用户对返回的查询结果集如何进行处理的,即不知道结果集的处理策略, 那么这个结果集的处理策略就让用户自己提供,query方法内部就调用用户提交的结果集处理策略进行处理, 为了能够让用户提供结果集的处理策略,需要对用户暴露出一个结果集处理接口ResultSetHandler, 结果集处理器接口ResultSetHandler的定义如下:

package me.gacl.util;

import java.sql.ResultSet;

/**
* @ClassName: ResultSetHandler
* @Description:结果集处理器接口
* @author: 孤傲苍狼
* @date: 2014-10-5 下午12:01:27
*
*/ 
public interface ResultSetHandler {

    /**
    * @Method: handler
    * @Description: 结果集处理方法
    * @Anthor:孤傲苍狼
    *
    * @param rs 查询结果集
    * @return
    */ 
    public Object handler(ResultSet rs);
}

  用户只要实现了ResultSetHandler接口,那么就是针对查询结果集写了一个处理器,在query方法内部就调用用户自己写的处理器处理结果集。

2.2、编写常用的结果集处理器

  为了提高框架的易用性,我们可以事先就针对结果集写好一些常用的处理器,比如将结果集转换成bean对象的处理器,将结果集转换成bean对象的list集合的处理器。

2.2.1、BeanHandler——将结果集转换成bean对象的处理器

package me.gacl.util;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

/**
* @ClassName: BeanHandler
* @Description: 将结果集转换成bean对象的处理器
* @author: 孤傲苍狼
* @date: 2014-10-5 下午12:00:33
*
*/ 
public class BeanHandler implements ResultSetHandler {
    private Class<?> clazz;
    public BeanHandler(Class<?> clazz){
        this.clazz = clazz;
    }
    public Object handler(ResultSet rs) {
        try{
            if(!rs.next()){
                return null;
            }
            Object bean = clazz.newInstance();
            //得到结果集元数据
            ResultSetMetaData metadata = rs.getMetaData();
            int columnCount = metadata.getColumnCount();//得到结果集中有几列数据
            for(int i=0;i<columnCount;i++){
                String coulmnName = metadata.getColumnName(i+1);//得到每列的列名
                Object coulmnData = rs.getObject(i+1);
                Field f = clazz.getDeclaredField(coulmnName);//反射出类上列名对应的属性
                f.setAccessible(true);
                f.set(bean, coulmnData);
            }
            return bean;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

2.2.2、BeanListHandler——将结果集转换成bean对象的list集合的处理器

package me.gacl.util;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
* @ClassName: BeanListHandler
* @Description: 将结果集转换成bean对象的list集合的处理器
* @author: 孤傲苍狼
* @date: 2014-10-5 下午12:00:06
*
*/ 
public class BeanListHandler implements ResultSetHandler {
    private Class<?> clazz;
    public BeanListHandler(Class<?> clazz){
        this.clazz = clazz;
    }

    public Object handler(ResultSet rs) {
        try{
            List<Object> list = new ArrayList<Object>();
            while(rs.next()){
                Object bean = clazz.newInstance();

                ResultSetMetaData  metadata = rs.getMetaData();
                int count = metadata.getColumnCount();
                for(int i=0;i<count;i++){
                    String name = metadata.getColumnName(i+1);
                    Object value = rs.getObject(name);

                    Field f = bean.getClass().getDeclaredField(name);
                    f.setAccessible(true);
                    f.set(bean, value);
                }
                list.add(bean);
            }
            return list.size()>0?list:null;

        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

  当框架自身提供的结果集处理器不满足用户的要求时,那么用户就可以自己去实现ResultSetHandler接口,编写满足自己业务要求的结果集处理器。

  有了上述的JdbcUtils框架之后,针对单个实体对象CRUD操作就非常方便了,如下所示:

package me.gacl.dao;

import java.sql.SQLException;
import java.util.List;
import me.gacl.domain.Account;
import me.gacl.util.BeanHandler;
import me.gacl.util.BeanListHandler;
import me.gacl.util.JdbcUtils;

public class AccountDao {

    public void add(Account account) throws SQLException{
        String sql = "insert into account(name,money) values(?,?)";
        Object params[] = {account.getName(),account.getMoney()};
        JdbcUtils.update(sql, params);
    }


    public void delete(int id) throws SQLException{
        String sql = "delete from account where id=?";
        Object params[] = {id};
        JdbcUtils.update(sql, params);
    }

    public void update(Account account) throws SQLException{

        String sql = "update account set name=?,money=? where id=?";
        Object params[] = {account.getName(),account.getMoney(),account.getId()};
        JdbcUtils.update(sql, params);

    }

    public Account find(int id) throws SQLException{
        String sql = "select * from account where id=?";
        Object params[] = {id};
        return (Account) JdbcUtils.query(sql, params, new BeanHandler(Account.class));
    }

    public List<Account> getAll() throws SQLException{
        String sql = "select * from account";
        Object params[] = {};
        return (List<Account>) JdbcUtils.query(sql, params,new BeanListHandler(Account.class));
    }
}

  编写的这个JDBC框架就是模拟Apache的DBUtils框架的实现,下一篇将具体介绍Apache的DBUtils框架。

  • 2
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值