一、利用反射及JDBC元数据编写通用的查询方法

1.ResultSetMetaData

定义:描述ResultSet的元数据对象,即从中可以获取到结果集中有多少列、列名是什么。

获取 ResultSetMetaData 对象:

  调用 ResultSet 的 getMetaData() 方法

方法:

  int getColumnCount():SQL语句中包含的列数

  String getColumnLabel(int column):获取指定的列的别名,其中索引从1开始

2.通用的查询方法

    /**
     * 通用的查询方法:可以根据传入的SQL、Class对象返回SQL对应的对象
     * 
     * @param clazz:
     *            描述对象的类型
     * @param sql:
     *            SQL语句,可能带占位符
     * @param args:
     *            填充占位符的可变参数
     */
    public <T> T get(Class<T> clazz, String sql, Object... args) {
        T entity = null;

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

        try {
            // 1.获得ResultSet对象
            conn = getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            rs = ps.executeQuery();

            // 2.得到ResultSetMetaData对象
            ResultSetMetaData rsmd = rs.getMetaData();

            // 3.创建一个Map<String, Object>对象,键:SQL查询的列的别名;值:列的值
            Map<String, Object> values = new HashMap<>();

            // 4.处理结果集,利用ResultSetMetaData填充3对应的Map对象
            if (rs.next()) {
                for (int i = 0; i < rsmd.getColumnCount(); i++) {
                    String columnLabel = rsmd.getColumnLabel(i + 1);
                    Object columnValue = rs.getObject(i + 1);

                    values.put(columnLabel, columnValue);
                }
            }

            // 5.若Map不为空集,利用反射创建 clazz 对应的对象
            if (values.size() > 0) {
                entity = clazz.newInstance();

                // 6.遍历Map对象,利用反射为Class对象的对应的属性赋值。
                for (Map.Entry<String, Object> entry : values.entrySet()) {
                    String fieldName = entry.getKey();
                    Object value = entry.getValue();
                    setFieldValue(entity, fieldName, value);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        return entity;
    }
    /**
     * 利用反射给具体对象的属性赋值
     */
    public void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class<? extends Object> clazz = obj.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

使用该通用方法:

    /**
     * 使用通用的查询方法
     */
    public void testGet(){
        String sql = "select id, name, email, birth " 
                + "from customers where id = ?";
        Customer customer = get(Customer.class, sql, 5);
        System.out.println(customer);
    }

 二、DAO设计模式

1.定义:

  DAO即 Data Access Object ,访问数据信息的类,包含了对数据的CRUD(create, read, update, delete),而不包含任何业务相关的信息。

2.作用:

  实现功能的模块化,更有利于代码的保护和升级。

3.方法:

void update(String sql, Object ... args); //INSERT,UPDATE,DELETE操作都可以包含在其中

    // INSERT,UPDATE,DELETE操作都可以包含在其中
    public void update(String sql, Object... args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            conn = JDBCTools.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            ps.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCTools.releaseDB(ps, null, conn);
        }
    }
public class JDBCTools {
    public static void releaseDB(Statement stat, ResultSet rs, Connection conn){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stat != null) {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static Connection getConnection() throws Exception{
        // 1.准备连接数据库的基本信息
                String driver = null;
                String jdbcUrl = null;
                String user = null;
                String password = null;
                // 读取类路径下的jdbc.properties文件
                InputStream in = JDBCTools.class.getClassLoader().getResourceAsStream("jdbc.properties");
                Properties properties = new Properties();
                properties.load(in);
                driver = properties.getProperty("driver");
                jdbcUrl = properties.getProperty("jdbcUrl");
                user = properties.getProperty("user");
                password = properties.getProperty("password");

                // 2.加载数据库驱动程序(对应的Driver实现类中有注册驱动的静态代码块)
                // DriverManager
                // .registerDriver(Class.forName(driverClass).newInstance));
                Class.forName(driver);
                // 3.通过DriverManager的getConnection()方法 获取数据库连接
                Connection conn = DriverManager.getConnection(jdbcUrl, user, password);
                return conn;
    }
}

<T> T get(Class<T> calzz, String sql, Object ... args); //查询一条记录,返回对应的对象

    // 查询一条记录,返回对应的对象
    public <T> T get(Class<T> clazz, String sql, Object... args) {
        List<T> result = getForList(clazz, sql, args);//查询多条记录的方法
        if(result.size() > 0){
            return result.get(0);
        }
        return null;
    }

<T> List<T> getForList(Class<T> clazz, String sql, Object ... args); //查询多条记录,返回对应的对象的集合

    // 查询多条记录,返回对应的对象的集合
    public <T> List<T> getForList(Class<T> clazz, String sql, Object... args) {
        List<T> list = new ArrayList<>();
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1.得到结果集
            conn = JDBCTools.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            rs = ps.executeQuery();
            //2.处理结果集,得到Map的List,其中一个Map对象对应一条记录
            List<Map<String, Object>> values = handleResultSetToMapList(rs);
            //3.把Map的List转为clazz对应的List
            list = transferMapListToBeanList(clazz, values);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCTools.releaseDB(ps, rs, conn);
        }
        return list;
    }
    
    /**
     * 将Map对应的List转换为clazz对应的List
     * @param clazz
     * @param values
     * @return
     * @throws Exception
     */
    private <T> List<T> transferMapListToBeanList(Class<T> clazz, List<Map<String, Object>> values)
            throws Exception {
        List<T> result = new ArrayList<T>();
        T bean = null;

        if (values.size() > 0) {
            for (Map<String, Object> m : values) {
                bean = clazz.newInstance();
                for (Map.Entry<String, Object> entry : m.entrySet()) {
                    String fieldName = entry.getKey();
                    Object value = entry.getValue();
                    JdbcTest.setFieldValue(bean, fieldName, value);
                }
                result.add(bean);
            }
        }
        return result;
    }

    /**
     * 处理结果集,得到Map的一个List,其中一个Map对象代表一条记录
     * 
     * @param rs
     * @return
     * @throws SQLException
     */
    private List<Map<String, Object>> handleResultSetToMapList(ResultSet rs) throws SQLException {
        List<String> columnLabels = getColumnLabels(rs);
        List<Map<String, Object>> values = new ArrayList<>();
        Map<String, Object> map = null;
        while (rs.next()) {
            map = new HashMap<>();
            for (String label : columnLabels) {
                Object ColumnValue = rs.getObject(label);
                map.put(label, ColumnValue);
            }
            values.add(map);
        }
        return values;
    }

    /**
     * 获取结果集的ColumnLabel(列的别名) 对应的List
     * 
     * @param rs
     * @return
     * @throws SQLException
     */
    private List<String> getColumnLabels(ResultSet rs) throws SQLException {
        List<String> labels = new ArrayList<>();
        ResultSetMetaData rsmd = rs.getMetaData();
        for (int i = 0; i < rsmd.getColumnCount(); i++) {
            labels.add(rsmd.getColumnLabel(i + 1));
        }
        return labels;
    }

<E> E getForValue(String sql, Object ... args); //返回某条记录的某一个字段的值或者一个统计的值(统计一共有多少条记录等)

    // 返回某条记录的某一个字段的值或者一个统计的值(统计一共有多少条记录或最大的值等)
    public <E> E getForValue(String sql, Object... args) {
        // 得到结果集:该结果集应该只有一行,且只有一列
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCTools.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            rs = ps.executeQuery();
            if (rs.next()) {
                return (E) rs.getObject(1);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCTools.releaseDB(ps, rs, conn);
        }
        return null;
    }

4.使用Beanutils工具类操作JavaBean

  JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,通过getter和setter方法来定义属性的get和set方法。

  可以通过一个apache的开源工具包 beanutils 来操作Java类的属性。

  要先导入commons-logging包和commons-beanutils包到类路径下。

  方法:setProperty(Object bean, String name, Object value):给对象bean的名字为name的属性赋value的值

       getProperty(Object bean, String name):返回对象bean的名为name的属性的值

  实例:

    public void BeanutilsTest() throws Exception{
        Object obj = new Customer();
        System.out.println(obj);
        //Customer [id=0, name=null, email=null, birth=null]
        BeanUtils.setProperty(obj, "id", "123");
        System.out.println(obj);
        //Customer [id=123, name=null, email=null, birth=null]
        Object value = BeanUtils.getProperty(obj, "id");
        System.out.println(value);//123
    }

  注:上面DAO的方法中利用反射给对象的属性赋值都可以换成beanutils的方法来赋值

//JdbcTest.setFieldValue(entity, fieldName, value);
BeanUtils.setProperty(entity, fieldName, value);

 三、JDBC的元数据

1.DataBaseMetaData

  是在Connection对象上获得的(DataBaseMetaData dbmd = connnection.getMetaData()),DataBaseMetaData类的对象实现了获得有关数据库管理系统的各种信息的方法 ,包括数据库的各个表,表中的各个列,数据类型,触发器,存储方面等各方面信息。

2.ResultSetMetaData

  是在ResultSet对象上获得的,实现了获得结果集的列数和列的别名等方法。

四、处理Blob

1.定义:

  LOB,即Large Objects(大对象),是用来存储大量的二进制和文本数据的一种数据类型(一个LOB字段可以存储多达4GB的数据)

2.分类:

  内部LOB:

    BLOB(二进制数据)

    CLOB(单字节字符数据)

    NCLOB(多字节字符数据)

  CLOB和NCLOB适合存储超长的文本数据。BLOB适合处理大量的二进制数据,如图像,视频,音频,文件等

  外部LOB:

    BFILE

  BFILE的数据是只读的,不参与事务。可帮助用户管理大量的由外部程序访问的文件。

3.实例:

    /**
     * 插入Blob类型的数据必须用PreparedStatement,因为Blob类型的数据无法使用字符串拼写
     */
    public void testInsertBlob() {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JDBCTools.getConnection();
            String sql = "INSERT INTO customers (NAME, EMAIL, BIRTH,PICTURE) " + "VALUES(?,?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "AAA");
            ps.setString(2, "abc@163.com");
            ps.setDate(3, new Date(new java.util.Date().getTime()));
            InputStream is = new FileInputStream("123.jpg");
            ps.setBlob(4, is);
            // 2)调用executeUpdate(sql)
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCTools.releaseDB(null, ps, conn);
        }
    }