美文网首页Java 杂谈每日一篇Java程序员技术栈
JDBC基础(二)-封装DDL\DML\DQL工具类

JDBC基础(二)-封装DDL\DML\DQL工具类

作者: sixleaves | 来源:发表于2018-08-14 20:20 被阅读40次

    1. 封装DDL\DML\DQL工具类

    • 上一篇博客做了什么。在上一篇博客中我分享了如何抽取JDBC工具类。该工具类提供数据库的会话创建关闭资源等我们操作数据库过程中用到的方法。
    • 本章博客做什么。在这一篇博客中我们分享下关于抽取操作数据库的DDLDMLDQL的工具类。

    1.1 为什么要自己抽取工具类,不用现成的

    • 有助于了解底层的实现。Hibernate和MyBatis底层很多都是基于反射来做ORM的
    • 锻炼自己的编码能力
    • 尝试思考如何设计框架

    1.2 封装DML和DDL工具方法

    我们首先要思考如何封装DML和DDL工具方法。DML指常用的数据库的增删改、DDL主要指建表改表语句.

    这两个类操作可以封装成一个方法,我们定义这个方法叫update

    • 它们这两类语句的目的都不是获取数据.
    • 他们本质都是sql

    update方法的设计

    • 方法参数。所以在设计方法的时候,我们可以很明确,需要传递字符串sql语句.并且因为我们用的是预编译执行体, 所以我们还需要传递要设置到预编译语句中的值。
    • 返回值。本质上我们可以返回执行是否成功给调用者,但是对于DML语句,我们可以返回受到影响的行数.如果执行失败我们就返回-1.对于DDL, 如果执行成功返回0, 执行失败返回-1.总之-1表示失败,非负数表示sql执行成功,并且受影响的行数有几行.
    • 方法体。考虑好参数、返回值。接着就是方法体如何写, 其实就是加载注册驱动, 建立会话连接, 创建预编译执行体, 执行sql.
    public class CommonUtil {
    
        public static int update(String sql, Object...values) throws SQLException {
            Connection conn = null;
            PreparedStatement ps = null;
            int rows = -1;
            try {
                conn = JDBCUtil.getConn();
    
                ps = conn.prepareStatement(sql);
                int len = values.length;
                for (int i = 0; i <len; i++) {
                    ps.setObject(i+1, values[i]);
                }
                rows = ps.executeUpdate();
            } finally {
                JDBCUtil.close(conn, ps);
                return rows;
            }
        }
    }
    

    1.3 封装通用查询方法

    DML和DDL的方法封装很简单。较为复杂的就是DQL方法的封装.因为对于DQL我们需要处理查询后返回的数据, 而这些数据如何处理对于不同的实体对象又不一样。我会分享如何一步步的抽取通用的查询方法。

    什么是ORM

    ORMObject Relation Mapping的缩写.在Web开发中其意思就是将数据库查询数据, 转换成程序中的对象的过程.做ORM的框架有很多, 比如Hibernate, Mybatis.

    query方法的设计

    先给方法取给通俗移动的方法名, 既然是查询,就叫query.

    • 参数设计. 和update道理一样, 我们肯定需要sql参数.
    • 返回值设计.既然是ORM,返回的当然一个相应的对象数组或者一个对象。因为查询可能是多条数据,可能是只要获取一条数据。

    1.3.1 ORM版本一,根据列索引获取数据

    先使用列索引, 在关于数据库的操作中, 索引都是从1开始计数.

        @Test
        public void testResultTest1() {
    
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
    
            String sql = "select id, name, age, gender, birthday from t_customer";
            List<Customer> customers = new ArrayList<>();
            try {
                conn = JDBCUtil.getConn();
                ps = conn.prepareStatement(sql);
                rs = ps.executeQuery();
    
                while (rs.next()) {
                    int id = rs.getInt(1);
                    String name = rs.getString(2);
                    int age = rs.getInt(3);
                    String gender = rs.getString(4);
                    Date birthday = rs.getDate(5);
                    customers.add(new Customer(id, name, age, gender,birthday));
                }
    
            }  catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(conn, ps, rs);
            }
    
    
            for (Customer c : customers) {
                System.out.println(c);
            }
        }
    

    1.3.2 ORM版本二,根据标签获取虚表数据

    由于使用列索引的方式, 是一种硬编码.只要当前查询语句的列一变,那么代码也得改.所以基于这种考虑。我们进行重构,改成基于虚表中的列名.结果集对象的getXxx(String)这个接口接受的就是虚表的列名而不是, 对于有别名的使用的是别名.

        @Test
        public void testResultTest2() {
    
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
    
            String sql = "select id, name, age, gender, birthday birth from t_customer";
            List<Customer> customers = new ArrayList<>();
            try {
                conn = JDBCUtil.getConn();
                ps = conn.prepareStatement(sql);
                rs = ps.executeQuery();
    
                while (rs.next()) {
                    // 使用虚表的列名
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    int age = rs.getInt("age");
                    String gender = rs.getString("gender");
                    Date birthday = rs.getDate("birth");
                    customers.add(new Customer(id, name, age, gender,birthday));
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(conn, ps, rs);
            }
    
            for (Customer c : customers) {
                System.out.println(c);
            }
        }
    

    1.3.3 ORM版本三,根据表元数据属性对象创建.

    在1.3.2 的这个版本中, 我们需要需要手动的指定列的名称.但是有一部分信息我们没有利用。

    1.3.2 存在的问题
    • 浪费了JavaBean类的信息JavaBean对象的信息, 或者称为模型对象domain对象.我们定义JavaBean对象的时候, 是按照查询的时候虚表的列名进行定义的.也就是JavaBean的属性名虚表的列名是一模一样的。我们可以通过反射的方式获取对象的属性对象.在设置属性对象的值,从而实现更加通用的代码。

    下面的代码用到了反射技术.主要步骤如下

    • 获取结果集元数据对象(即描述结果集的对象,结果集其实就是虚表)
    • 根据结果集元数据对象获取结果集中的行数.
    • 根据结果集元数据对象获取每一列的列名.
        @Test
        public void testResultTest3_metadata() {
    
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
    
            String sql = "select id, name, age, gender, birthday birth from t_customer";
            List<Customer> customers = new ArrayList<>();
            try {
                conn = JDBCUtil.getConn();
                ps = conn.prepareStatement(sql);
                rs = ps.executeQuery();
                ResultSetMetaData rsmd = ps.getMetaData();
    
                // 获取列的总个数
                int rolCount = rsmd.getColumnCount();
    
                // 获取原始列表的列名,这个列名不是虚表名
                for (int i = 0; i < rolCount; i++) {
    //                System.out.print(rsmd.getColumnName(i+1) + "\t");
                }
    
    //            System.out.println();
                // 获取虚表的列名, 使用列标签
                ArrayList<String> colLabels = new ArrayList<>();
                for (int i = 0; i < rolCount; i++) {
                    colLabels.add(rsmd.getColumnLabel(i+1));
                }
                while (rs.next()) {
                    // 使用虚表的列名
                    // 元数据 ---- 描述数据的数据称为元数据.获取表的描述数据
                    for (int i = 0; i < rolCount; i++) {
    
                       Object value = rs.getObject(colLabels.get(i));
                       System.out.print(value + "\t");
                    }
                    System.out.println();
                }
    
            }  catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(conn, ps, rs);
            }
    
            for (Customer c : customers) {
                System.out.println(c);
            }
        }
    

    1.3.4 ORM版本四,根据反射泛型封装通用查询版本

    1.3.3 版本的问题
    • 每种类型都要再写一套query方法。1.3.3的代码只使用于Customer类, 我们可以使用泛型技术, 进一步抽取代码,将类的类型定义成一种类型.在这里我们将要返回的JavaBean类型定义为一种泛型类型。
    
        public static <T> List<T> query(Class<T> clazz, String sql, Object...values)
                throws IllegalAccessException, SQLException, NoSuchFieldException,
                InstantiationException, ClassNotFoundException {
    
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<T> beans = new ArrayList<T>();
            try {
                conn = JDBCUtil.getConn();
                ps = conn.prepareStatement(sql);
    
                int valuesCount = values.length;
                for (int i = 0; i < valuesCount; i++) {
                    ps.setObject(i+1, values[i]);
                }
    
                rs = ps.executeQuery();
                ResultSetMetaData rsmd = ps.getMetaData();
    
                // 获取列的总个数
                int rolCount = rsmd.getColumnCount();
    
                while (rs.next()) {
    
                    T bean = clazz.newInstance();
    
                    // 使用虚表的列名
                    // 元数据 ---- 描述数据的数据称为元数据.获取表的描述数据
                    for (int i = 0; i < rolCount; i++) {
    
                        String label = rsmd.getColumnLabel(i+1);
                        Field field = Customer.class.getDeclaredField(label);
                        field.setAccessible(true);
                        Object value = rs.getObject(label);
                        field.set(bean, value);
                    }
                    beans.add(bean);
                }
            }  finally {
                JDBCUtil.close(conn, ps, rs);
            }
            return beans;
        }
    

    最终封装好的CommonUtils.

    
    public class CommonUtil {
    
        public static int update(String sql, Object...values) throws SQLException {
            Connection conn = null;
            PreparedStatement ps = null;
            int rows = -1;
            try {
                conn = JDBCUtil.getConn();
    
                ps = conn.prepareStatement(sql);
                int len = values.length;
                for (int i = 0; i <len; i++) {
                    ps.setObject(i+1, values[i]);
                }
                rows = ps.executeUpdate();
            } finally {
                JDBCUtil.close(conn, ps);
                return rows;
            }
        }
    
        public static <T> List<T> query(Class<T> clazz, String sql, Object...values)
                throws IllegalAccessException, SQLException, NoSuchFieldException,
                InstantiationException, ClassNotFoundException {
    
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<T> beans = new ArrayList<T>();
            try {
                conn = JDBCUtil.getConn();
                ps = conn.prepareStatement(sql);
    
                int valuesCount = values.length;
                for (int i = 0; i < valuesCount; i++) {
                    ps.setObject(i+1, values[i]);
                }
    
                rs = ps.executeQuery();
                ResultSetMetaData rsmd = ps.getMetaData();
    
                // 获取列的总个数
                int rolCount = rsmd.getColumnCount();
    
                while (rs.next()) {
    
                    T bean = clazz.newInstance();
    
                    // 使用虚表的列名
                    // 元数据 ---- 描述数据的数据称为元数据.获取表的描述数据
                    for (int i = 0; i < rolCount; i++) {
    
                        String label = rsmd.getColumnLabel(i+1);
                        Field field = Customer.class.getDeclaredField(label);
                        field.setAccessible(true);
                        Object value = rs.getObject(label);
                        field.set(bean, value);
                    }
                    beans.add(bean);
                }
            }  finally {
                JDBCUtil.close(conn, ps, rs);
            }
            return beans;
        }
    }
    

    后续

    请继续关注JDBC基础(三).下篇我将分享的是

    • 在JDBC中如何操作事务
    • 开发中应用数据库连接池

    相关文章

      网友评论

        本文标题:JDBC基础(二)-封装DDL\DML\DQL工具类

        本文链接:https://www.haomeiwen.com/subject/myzpbftx.html