zoukankan      html  css  js  c++  java
  • JDBC框架的编写

    JDBC框架
    直接使用JDBC开发,非常麻烦,步骤繁琐,所以在企业开发Web程序时,连接数据库进行操作都是通过JDBC框架来完成的
    知识点
    1,JDBC元数据API(编写JDBC框架基础)
    2,框架编写思想
    3,Apache DBUtils(tomcat的JDBC框架)

    数据库元数据(metadata)
    数据库存储结构定义信息(库,表,记录,列的定义信息)
    DataBaseMetaData数据库元数据
    Connection.getMetaData();通过连接获得数据库元数据信息
    getURL():返回一个String类对象,代表数据库的URL。
    getUserName():返回连接当前数据库管理系统的用户名。
    getDriverName():返回驱动驱动程序的名称。
    getPrimaryKeys(String catalog, String schema, String table):返回指定表主键的结果集

    参数元数据
    ParameterMetaData
    通过预编译对象PreparedStatement获得
    String sql="select * from users where username=? and password=?";
    PreparedStament preparedStatement=conn.preparedStatement(sql);
    preparedStatement.getParameterMetaData();//通过参数元数据对象可以获得?参数的信息。
    getParameterCount() 获得指定参数的个数
    getParameterType(int param) 获得指定参数的sql类型
    注意getParameterType方法并不是所有的数据库都支持mysql就不支持,回报异常,异常处理 Parameter metadata not available for the given statement
    在数据库连接池配置文件中url后面拼接参数 ?generateSimpleParameterMetadata=true,但是即使是这样该方法的返回值都是varchar

    结果集元数据
    ResultSetMetaData
    ResultSet. getMetaData() 获得代表ResultSet对象元数据的ResultSetMetaData对象。
    getColumnCount() 返回resultset对象的列数
    getColumnName(int column) 获得指定列的名称  
    getColumnTypeName(int column) 获得指定列的类型

    使用元数据简化JDBC代码
    在实际业务背景下,系统中所有实体对象都涉及到基本的CRUD操作;所有的CUD操作代码基本相同,仅仅是发送给数据库的SQL语句不同而已,因此可以把操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句
    当然实体的R操作除了SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此一个query方法,除了以参数形式接收变化的SQL语句之外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet的数据映射到实体对象中。
    DAO模式结合自定义JDBC框架framework

    DAO模式编写JvaBean,Account类

    public class Account {
        private int id;
        private String name;
        private double money;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    
    }

    在未使用框架时编写AccountDAO

    public void insertAccount(Account account) {
            Connection conn = null;
            PreparedStatement stmt = null;
    
            try {
                conn = JDBCUtils.getConnection();
                String sql = "insert into account values(null,?,?)";
    
                stmt = conn.prepareStatement(sql);
                // 设置参数
                stmt.setString(1, account.getName());
                stmt.setDouble(2, account.getMoney());
    
                stmt.executeUpdate();
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtils.release(stmt, conn);
            }
        }

    通过不同对象DAO模式下的SQL操作比较对方法进行提取编写通用框架

    public static void update(String sql, Object... args) {
            Connection conn = null;
            PreparedStatement stmt = null;
    
            try {
                conn = JDBCUtils.getConnection();
    
                stmt = conn.prepareStatement(sql);
                // 设置参数 --- 根据?设置参数
                ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
                int count = parameterMetaData.getParameterCount();
                for (int i = 1; i <= count; i++) {
                    stmt.setObject(i, args[i - 1]);
                }
    
                stmt.executeUpdate();
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtils.release(stmt, conn);
            }
        }

    在使用框架之后写的AccountDAO

    public void insertAccount(Account account) {
        String sql = "insert into account values(null,?,?)";
        Object[] params={account.getNmae(),account.getMoney()}
        JDBCFramework.update(sql,params)
        
    
    }

    通用框架的编写使用在后来可以不断重复使用,大大的简化了开发。但是上面的通用框架方法只适合CUD增删改,对于查询R来说是不能使用的,所以需要单独进行抽取编写框架方法

    在未使用框架时

    public Account findById(int id) {
         Connection conn = null;
         PreparedStatement stmt = null;
         ResultSet rs = null;
        Account account = null;
        
         try {
        conn = JDBCUtils.getConnection();
        String sql = "select * from account where id = ?";
        
         stmt = conn.prepareStatement(sql);
        
        //设置参数
        stmt.setInt(1, id);
        
         rs = stmt.executeQuery();
        if (rs.next()) {
        account = new Account();
        account.setId(rs.getInt("id"));
        account.setName(rs.getString("name"));
         account.setMoney(rs.getDouble("money"));
        }
    
        } catch (SQLException e) {
        e.printStackTrace();
         } finally {
        JDBCUtils.release(rs, stmt, conn);
         }
        
         return account;
         }

    编写框架

    public static <T> T query(String sql, MyResultSetHandler<T> handler,
                Object... args) {
            T obj = null;
    
            Connection conn = null;
            PreparedStatement stmt = null;
            ResultSet rs = null;
    
            try {
                conn = JDBCUtils.getConnection();
                stmt = conn.prepareStatement(sql);
    
                // 设置参数
                ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
                int count = parameterMetaData.getParameterCount();
                for (int i = 1; i <= count; i++) {
                    stmt.setObject(i, args[i - 1]);
                }
    
                rs = stmt.executeQuery();
                obj = handler.handle(rs);
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtils.release(rs, stmt, conn);
            }
            return obj;
        }

    要另外定义接口将rs结果集中的数据封装成一个对象

    public interface MyResultSetHandler<T> {
        // 将rs中数据封装对象
        public T handle(ResultSet rs);
    }

    使用框架之后

    public Account findById(int id) {
            // 使用自定义框架
            String sql = "select * from account where id = ?";
            MyResultSetHandler handler = new MyResultSetHandler() {//内部类实现框架的接口封装数据。但是每次都要实现 MyResultSetHandler接口,手动完成数据的封装的动作,所以此动作依然可以继续向上抽取。
                @Override
                public Object handle(ResultSet rs) {
                    try {
                        if (rs.next()) {
                            Account account = new Account();
                            account.setId(rs.getInt("id"));
                            account.setName(rs.getString("name"));
                            account.setMoney(rs.getDouble("money"));
                            return account;
                        }
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
            };
    
            return (Account) JDBCFramework.query(sql, handler, id);
        }
    内部类实现框架的接口封装数据。但是每次都要实现 MyResultSetHandler接口,手动完成数据的封装的动作,所以此动作依然可以继续向上抽取。
    将封装结果集数据的动作进行向上提取,利用泛型和反射在框架中编写通用的Handler程序,☆难点。
    public class MyBeanHandler<T> implements MyResultSetHandler<T> {
    
        private Class<T> domainClass;
    
        public MyBeanHandler(Class<T> domainClass) {//泛型是存在编译时期的,要创建T泛型的实例,需要将具体类的字节码文件对象传入利用反射技术去创建对象。
            this.domainClass = domainClass;
        }
    
        @Override
        public T handle(ResultSet rs) {//用泛型去指代返回的数据类型。
            try {
                ResultSetMetaData resultSetMetaData = rs.getMetaData();// 结果集元数据
                int count = resultSetMetaData.getColumnCount();
    
                BeanInfo beanInfo = Introspector.getBeanInfo(domainClass);//使用内省技术获得字节码文件对象的属性描述器。
                PropertyDescriptor[] descriptors = beanInfo
                        .getPropertyDescriptors();
                if (rs.next()) {
                    T t = domainClass.newInstance();//此处获得T的实例对象
                    for (int i = 1; i <= count; i++) {
                        String columnName = resultSetMetaData.getColumnName(i);
                        // 获得列名 --- 需要去查找匹配属性
                        for (PropertyDescriptor propertyDescriptor : descriptors) {
                            if (columnName.equals(propertyDescriptor.getName())) {
                                // 列名 存在 同名属性 ---- 列值 存到属性里
                                Method writeMethod = propertyDescriptor
                                        .getWriteMethod(); // setName setMoney
                                writeMethod.invoke(t, rs.getObject(columnName));
                            }
                        }
                    }
                    return t;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    }

    最后简化出的DAO下的查询方法

    public Account findById(int id) {
            String sql = "select * from account where id = ?";
            return JDBCFramework.query(sql,new MyBeanHandler<Account>(Account.class),id)
    }
  • 相关阅读:
    24点游戏 程序(一)
    24点游戏全解-1362组
    Android版 hanoi 汉诺塔 源码
    24点游戏 程序(二)
    Javascript 面向对象编程
    新浪微博 OAuth2 NodeJs发微博
    AT5800 [AGC043C] Giant Graph 题解
    CF1033D Divisors 题解
    CF1033E Hidden Bipartite Graph 题解
    AT???? [ABC255D] String Cards 题解
  • 原文地址:https://www.cnblogs.com/ss561/p/4646426.html
Copyright © 2011-2022 走看看