反射实例之封装对数据库的增删改查操作

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反射实例之封装对数据库的增删改查操作相关的知识,希望对你有一定的参考价值。

需求

使用反射来封装对mysql数据库的增删改查操作,封装更通用的方法。

代码实现

第一步,创建数据库连接。

设置数据库连接参数,获取Connection。注意,改成自己数据库的连接参数。

/**
 * 获取数据库连接的工具类,注意在该类设置数据库连接参数
 */
class JDBCUtil {
    /**
     * 获取数据库连接
     *
     * @return 数据库连接对象
     */
    public static Connection getConnection() {
        Connection connection = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost/test";
            String user = "root";
            String password = "root";
            connection = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }
}

第二步,创建BaseMapper接口。

该接口中定义了众多增删改查的默认方法。

public interface BaseMapper<T> {

    // --------------------------------------插入操作------------------------------------------

    /**
     * 向数据库内插入一条记录。
     * 插入SQL语句如例:insert into user(id,username,password,phone,email,memo,insert_time) values(20,'刘三刀','123456','1008611','1008611@163.com','我是一名用户','2021-08-10 21:28:35')
     *
     * @param t 带插入的实体类对象
     * @return 返回受影响行数
     * @throws IllegalAccessException
     * @throws SQLException
     */
    default int insert(T t) throws IllegalAccessException, SQLException {
        // 使用泛型参数,是因为不清楚会传什么实体类,所以使用泛型
        // 获取输入参数t的Class类对象
        Class<?> tClass = t.getClass();
        // 使用StringBuilder来存放SQL语句
        StringBuilder sb = new StringBuilder();
        // 插入SQL语句的头部"insert into "
        sb.append("insert into ");
        // 获取类名,但需要将类名转换成小写,虽然在MySQL中不区分大小写
        sb.append(tClass.getSimpleName().toLowerCase());
        sb.append("(");
        // 获取属性数组,即获取t所表示的实体类中的所有属性,而不是该类中的所有方法
        Field[] fields = tClass.getDeclaredFields();
        int i = 0;
        for (Field field : fields) {
            sb.append(field.getName());// 添加属性名
            sb.append(",");
            i++;
        }
        // 除去最后一个逗号,添加一个右括号
        String s = sb.substring(0, sb.length() - 1) + ")";
        StringBuilder result = new StringBuilder(s);
        result.append(" values(");
        for (int j = 0; j < i; j++) {
            result.append("?,");
        }
        // 到这一步,一个完整的SQL语句完成了,只是没有传入参数
        // 例如:insert into user(phone,username,insert_time,email,memo,id,password) values(?,?,?,?,?,?,?)
        String sql = result.substring(0, result.length() - 1) + ")";

        // 接下来就是进行插入操作
        // 获取数据库连接
        Connection connection = JDBCUtil.getConnection();
        // 根据Connection创建PreparedStatement对象
        PreparedStatement ps = connection.prepareStatement(sql);
        // 该参数表示是第几个待传入的参数,在PreparedStatement类中设置参数对应值的方法的序号是从1开始的
        int j = 1;
        for (Field field : fields) {
            field.setAccessible(true);
            ps.setObject(j, field.get(t));
            j++;
        }
        // 可以打印完整的SQL语句
        System.out.println(ps);
        // 执行SQL插入操作
        return ps.executeUpdate();
    }

    // --------------------------------------更新操作------------------------------------------

    /**
     * 通过id字段更新记录
     * SQL语句如:update user set username='王五' where id=6;
     *
     * @param t 待更新的信息,都是封装在实体类中的,包括id信息
     * @return 返回受影响行数
     */
    default int updateById(T t) throws SQLException, IllegalAccessException {
        // 1.获取字段
        // 获取t所表示类的Class对象
        Class<?> tClass = t.getClass();
        // 获取该类的所有属性
        Field[] fields = tClass.getDeclaredFields();
        // 过滤掉空值的字段
        // 将Field数组转换成Field流
        Stream<Field> stream = Arrays.stream(fields);
        // 过滤掉空属性,因为待更新的字段必须是有值的属性
        Stream<Field> fieldStream = stream.filter((field) -> {
            field.setAccessible(true);
            try {
                // 只保留不为空的字段
                return field.get(t) != null;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return false;
        });

        // 2.组装SQL语句
        StringBuilder sb = new StringBuilder();
        sb.append("update ");
        sb.append(tClass.getSimpleName().toLowerCase());// 表名
        sb.append(" set ");
        // 循环遍历要更新的字段
        List<Field> fieldList = new ArrayList<>();
        fieldStream.forEach((field -> {
            // 将字段添加到List集合中
            fieldList.add(field);
            if (!"id".equals(field.getName())) {
                sb.append(field.getName().toLowerCase());
                sb.append("=");
                sb.append("?");
                sb.append(",");
            }
        }));
        // 去除最后一个逗号","
        String where = sb.substring(0, sb.length() - 1);
        // 设置where后面的条件子句,即"where id=?;"
        StringBuilder sql = new StringBuilder(where);
        sql.append(" where ");
        sql.append("id=?;");
        String updateSql = sql.toString();

        // 3.执行SQL语句返回结果
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(updateSql);
        // 调用PreparedStatement类的setObject()方法的参数序号,从1开始
        int parameterIndex = 1;
        // id字段对应的值
        Object id = -1;
        for (Field field : fieldList) {
            if (!"id".equals(field.getName())) {
                field.setAccessible(true);
                ps.setObject(parameterIndex, field.get(t));
                parameterIndex++;
            } else {
                // 将id字段的值保存到局部变量中
                id = field.get(t);
            }
        }
        // 设置id字段的值,即最后一个问号的值
        ps.setObject(parameterIndex, id);
        System.out.println(ps);
        // 执行更新操作,返回受影响行数
        return ps.executeUpdate();
    }

    /**
     * 通过条件查询更新记录。
     * SQL语句如:update user set username='李四',password='abcd' where id=23 and username='王五' and email='10086@qq.com';
     *
     * @param t          待更新的信息
     * @param conditions where子句后面的条件
     * @return 返回受影响的行数
     * @throws SQLException
     * @throws IllegalAccessException
     */
    default int update(T t, Map<String, Object> conditions) throws SQLException, IllegalAccessException {
        // 1.获取字段
        // 获取t所表示类的Class对象
        Class<?> tClass = t.getClass();
        // 获取该类的所有属性
        Field[] fields = tClass.getDeclaredFields();
        // 过滤掉空值的字段
        // 将Field数组转换成Field流
        Stream<Field> stream = Arrays.stream(fields);
        // 过滤掉空属性,因为待更新的字段必须是有值的属性
        Stream<Field> fieldStream = stream.filter((field) -> {
            field.setAccessible(true);
            try {
                // 只保留不为空的字段
                // 注意:如果是基本数据类型如int,那么会有一个默认值,会通过!=null判断,导致出现问题
                // 所以在实体类中尽量使用引用数据类型,而不是基本数据类型
                return field.get(t) != null;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return false;
        });

        // 2.组装SQL语句
        StringBuilder sb = new StringBuilder();
        sb.append("update ");
        sb.append(tClass.getSimpleName().toLowerCase());// 表名
        sb.append(" set ");
        // 循环遍历要更新的字段
        List<Field> fieldList = new ArrayList<>();
        fieldStream.forEach((field -> {
            // 将字段添加到List集合中
            fieldList.add(field);
            sb.append(field.getName().toLowerCase());
            sb.append("=");
            sb.append("?");
            sb.append(",");
        }));
        // 去除最后一个逗号","
        String where = sb.substring(0, sb.length() - 1);
        // 设置where后面的条件子句,即"where username='xxx' and password='xxx';"
        StringBuilder sql = new StringBuilder(where);
        sql.append(" where ");
        for (String key : conditions.keySet()) {
            sql.append(key);
            sql.append("=?");
            sql.append(" and ");
        }
        String updateSql = sql.substring(0, sql.lastIndexOf("and"));

        // 3.执行SQL语句返回结果
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(updateSql);
        // 调用PreparedStatement类的setObject()方法的参数序号,从1开始
        int parameterIndex = 1;
        // 设置set子句后面的参数
        for (Field field : fieldList) {
            field.setAccessible(true);
            ps.setObject(parameterIndex, field.get(t));
            parameterIndex++;
        }
        // 设置where子句后面的参数
        for (String key : conditions.keySet()) {
            ps.setObject(parameterIndex, conditions.get(key));
            parameterIndex++;
        }
        System.out.println(ps);
        // 执行更新操作,返回受影响行数
        return ps.executeUpdate();
    }

    // --------------------------------------删除操作------------------------------------------

    /**
     * 通过id字段删除某条记录。
     * SQL语句如:delete from user where id=2;
     *
     * @param clazz 实体类,如User.class
     * @param id    待删除的id字段值
     * @return 返回受影响行数
     * @throws SQLException
     */
    default int deleteById(Class<?> clazz, Serializable id) throws SQLException {
        // 1.拼接SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("delete from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        sql.append(" where ");
        sql.append("id=?;");
        String deleteSql = sql.toString();

        // 2.执行SQL语句
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(deleteSql);
        ps.setObject(1, id);
        return ps.executeUpdate();
    }

    /**
     * 多条件删除。
     * SQL语句如:delete from user where username='zhangsan' and password='123456';
     *
     * @param clazz      实体类,如User.class
     * @param conditions where子句后面跟着的条件
     * @return 返回受影响行数
     * @throws SQLException
     */
    default int deleteByMap(Class<?> clazz, Map<String, Object> conditions) throws SQLException {
        // 1.拼接SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("delete from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        sql.append(" where ");
        // 从conditions集合中读取条件参数
        for (String key : conditions.keySet()) {
            sql.append(key);
            sql.append("=");
            sql.append("?");
            sql.append(" and ");
        }
        String deleteSql = sql.substring(0, sql.lastIndexOf("and"));

        // 2.执行SQL语句
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(deleteSql);
        int parameterIndex = 1;
        for (String key : conditions.keySet()) {
            ps.setObject(parameterIndex, conditions.get(key));
            parameterIndex++;
        }
        return ps.executeUpdate();
    }

    /**
     * 根据实体类中的字段进行条件删除。
     * SQL语句如:delete from user where username='zhangsan' and password='123456';
     *
     * @param t 实体类对象
     * @return 返回受影响行数
     * @throws SQLException
     * @throws IllegalAccessException
     */
    default int delete(T t) throws SQLException, IllegalAccessException {
        // 1.获取条件字段,过滤掉空字段
        Field[] fields = t.getClass().getDeclaredFields();
        // 使用Stream流进行初步过滤
        Stream<Field> stream = Arrays.stream(fields);
        Stream<Field> fieldStream = stream.filter((field -> {
            field.setAccessible(true);
            try {
                // 只保留设置了值的字段
                return field.get(t) != null;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return false;
        }));

        // 2.组装SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("delete from ");
        sql.append(t.getClass().getSimpleName().toLowerCase());
        sql.append(" where ");
        List<Field> fieldList = new ArrayList<>();
        fieldStream.forEach((field -> {
            fieldList.add(field);
            field.setAccessible(true);
            sql.append(field.getName().toLowerCase());
            sql.append("=");
            sql.append("?");
            sql.append(" and ");
        }));
        String deleteSql = sql.substring(0, sql.lastIndexOf("and"));

        // 3.执行SQL语句,返回删除结果
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(deleteSql);
        int parameterIndex = 1;
        for (Field field : fieldList) {
            ps.setObject(parameterIndex, field.get(t));
            parameterIndex++;
        }
        return ps.executeUpdate();
    }

    // --------------------------------------查询操作------------------------------------------

    /**
     * 通过id查询某条记录。
     * SQL语句如:select * from user where id=2;
     *
     * @param clazz 实体类,如User.class
     * @param id    主键id
     * @return 返回查询结果
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    default T selectById(Class<?> clazz, Serializable id) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 1.组装SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("select * from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        sql.append(" where id=?");
        String selectSql = sql.toString();

        // 2.执行SQL语句获取查询结果集
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(selectSql);
        ps.setObject(1, id);
        ResultSet rs = ps.executeQuery();

        // 3.封装查询结果集到实体类对象中并返回
        // 利用反射创建实例对象
        Object t = clazz.newInstance();
        Method[] methods = t.getClass().getDeclaredMethods();
        if (rs.next()) {
            // 循环遍历方法数组,将数据封装到实体类对象中
            for (Method method : methods) {
                // 过滤setXXX方法,设置属性的值
                if (method.getName().startsWith("set")) {
                    // 设置属性的值,通过rs.getObject(String columnLabel)方法获取指定列的值
                    method.invoke(t, rs.getObject(method.getName().substring(3)));
                }
            }
        }
        // 如果还有下一条记录,表示查询结果有多个,那么不应该
        if (rs.next()) {
            throw new IllegalArgumentException("查询结果有多个,请输入合法参数:" + id);
        }
        return (T) t;
    }

    /**
     * 通过批量id查询多条记录。
     * SQL语句如:select * from user where id in (1,2,3,4,5);
     *
     * @param clazz  实体类,如User.class
     * @param idList 主键id列表
     * @return 返回查询结果集
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    default List<T> selectBatchIds(Class<?> clazz, List<? extends Serializable> idList) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 1.组装SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("select * from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        sql.append(" where id in(");
        for (int i = 0; i < idList.size(); i++) {
            sql.append("?");
            if (i != idList.size() - 1) {
                sql.append(",");
            }
        }
        sql.append(")");
        String selectSql = sql.toString();

        // 2.执行SQL语句获取查询结果集
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(selectSql);
        int parameterIndex = 1;
        for (Serializable id : idList) {
            ps.setObject(parameterIndex, id);
            parameterIndex++;
        }
        ResultSet rs = ps.executeQuery();

        // 3.封装查询结果集到实体类对象中并返回
        List<T> tList = new ArrayList<>();
        while (rs.next()) {
            // 利用反射创建实例对象
            Object t = clazz.newInstance();
            // 获取该类的所有声明的方法
            Method[] methods = t.getClass().getDeclaredMethods();
            // 循环遍历方法数组,将数据封装到实体类对象中
            for (Method method : methods) {
                // 过滤setXXX方法,设置属性的值
                if (method.getName().startsWith("set")) {
                    // 设置属性的值,通过rs.getObject(String columnLabel)方法获取指定列的值
                    method.invoke(t, rs.getObject(method.getName().substring(3)));
                }
            }
            tList.add((T) t);
        }
        return tList;
    }

    /**
     * 多条件查询唯一一条记录。
     * SQL语句如:select * from user where username='zhangsan' and password='123456';
     *
     * @param clazz      实体类,如User.class
     * @param conditions where子句后面的条件集合
     * @return 返回查询结果
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    default T selectOne(Class<?> clazz, Map<String, Object> conditions) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 1.组装SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("select * from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        // where子句后面的条件
        sql.append(" where ");
        for (String key : conditions.keySet()) {
            sql.append(key);
            sql.append("=");
            sql.append("?");
            sql.append(" and ");
        }
        String selectSql = sql.substring(0, sql.lastIndexOf("and"));

        // 2.执行SQL语句获取查询结果集
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(selectSql);
        int parameterIndex = 1;
        for (String key : conditions.keySet()) {
            ps.setObject(parameterIndex, conditions.get(key));
            parameterIndex++;
        }
        ResultSet rs = ps.executeQuery();

        // 3.封装查询结果集到实体类对象中并返回
        // 利用反射创建实例对象
        Object t = clazz.newInstance();
        Method[] methods = t.getClass().getDeclaredMethods();
        if (rs.next()) {
            // 循环遍历方法数组,将数据封装到实体类对象中
            for (Method method : methods) {
                // 过滤setXXX方法,设置属性的值
                if (method.getName().startsWith("set")) {
                    // 设置属性的值,通过rs.getObject(String columnLabel)方法获取指定列的值
                    method.invoke(t, rs.getObject(method.getName().substring(3)));
                }
            }
        }
        // 如果还有下一条记录,表示查询结果有多个,那么不应该
        if (rs.next()) {
            throw new IllegalArgumentException("查询结果有多个,请输入合法参数。");
        }
        return (T) t;
    }

    /**
     * 指定条件下的记录总条数。
     * SQL语句如:select count(*) from user where password='123456';
     *
     * @param clazz      实体类,如User.class
     * @param conditions where子句后面的条件集合
     * @return 返回记录总条数
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    default Integer selectCount(Class<?> clazz, Map<String, Object> conditions) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 1.组装SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("select count(*) from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        // where子句后面的条件
        sql.append(" where ");
        for (String key : conditions.keySet()) {
            sql.append(key);
            sql.append("=");
            sql.append("?");
            sql.append(" and ");
        }
        String selectSql = sql.substring(0, sql.lastIndexOf("and"));

        // 2.执行SQL语句获取查询结果集
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(selectSql);
        int parameterIndex = 1;
        for (String key : conditions.keySet()) {
            ps.setObject(parameterIndex, conditions.get(key));
            parameterIndex++;
        }
        ResultSet rs = ps.executeQuery();

        // 3.封装查询结果集到实体类对象中并返回
        // 记录的总条数
        int count = 0;
        if (rs.next()) {
            count = rs.getInt(1);
        }
        return count;
    }

    /**
     * 多条件下查询多条记录。
     * SQL语句如下:select * from user where username='zhangsan' and password='123456';
     *
     * @param clazz      实体类,如User.class
     * @param conditions where子句后面的条件集合
     * @return 返回查询结果集
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    default List<T> selectList(Class<?> clazz, Map<String, Object> conditions) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 1.组装SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("select * from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        // where子句后面的条件
        sql.append(" where ");
        for (String key : conditions.keySet()) {
            sql.append(key);
            sql.append("=");
            sql.append("?");
            sql.append(" and ");
        }
        String selectSql = sql.substring(0, sql.lastIndexOf("and"));

        // 2.执行SQL语句获取查询结果集
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(selectSql);
        int parameterIndex = 1;
        for (String key : conditions.keySet()) {
            ps.setObject(parameterIndex, conditions.get(key));
            parameterIndex++;
        }
        ResultSet rs = ps.executeQuery();

        // 3.封装查询结果集到实体类对象中并返回
        List<T> tList = new ArrayList<>();
        while (rs.next()) {
            // 利用反射创建实例对象
            Object t = clazz.newInstance();
            Method[] methods = t.getClass().getDeclaredMethods();
            // 循环遍历方法数组,将数据封装到实体类对象中
            for (Method method : methods) {
                // 过滤setXXX方法,设置属性的值
                if (method.getName().startsWith("set")) {
                    // 设置属性的值,通过rs.getObject(String columnLabel)方法获取指定列的值
                    method.invoke(t, rs.getObject(method.getName().substring(3)));
                }
            }
            tList.add((T) t);
        }
        return tList;
    }

    /**
     * 多条件下分页查询多条记录。
     * SQL语句如下:select * from user where username='zhangsan' limit 0,5;
     *
     * @param clazz      实体类,如User.class
     * @param limit      分页查询中的起始索引,从0开始
     * @param size       该页条数
     * @param conditions where子句后面的条件集合
     * @return 返回查询结果集
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    default List<T> selectPage(Class<?> clazz, int limit, int size, Map<String, Object> conditions) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 1.组装SQL语句
        StringBuilder sql = new StringBuilder();
        sql.append("select * from ");
        sql.append(clazz.getSimpleName().toLowerCase());
        // where子句后面的条件,注意处理空条件的问题
        sql.append(" where ");
        for (String key : conditions.keySet()) {
            sql.append(key);
            sql.append("=");
            sql.append("?");
            sql.append(" and ");
        }
        String selectSql = sql.substring(0, sql.lastIndexOf("and"));
        selectSql = selectSql + " limit " + limit + "," + size;

        // 2.执行SQL语句获取查询结果集
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(selectSql);
        int parameterIndex = 1;
        for (String key : conditions.keySet()) {
            ps.setObject(parameterIndex, conditions.get(key));
            parameterIndex++;
        }
        ResultSet rs = ps.executeQuery();

        // 3.封装查询结果集到实体类对象中并返回
        List<T> tList = new ArrayList<>();
        while (rs.next()) {
            // 利用反射创建实例对象
            Object t = clazz.newInstance();
            Method[] methods = t.getClass().getDeclaredMethods();
            // 循环遍历方法数组,将数据封装到实体类对象中
            for (Method method : methods) {
                // 过滤setXXX方法,设置属性的值
                if (method.getName().startsWith("set")) {
                    // 设置属性的值,通过rs.getObject(String columnLabel)方法获取指定列的值
                    method.invoke(t, rs.getObject(method.getName().substring(3)));
                }
            }
            tList.add((T) t);
        }
        return tList;
    }

    /**
     * 查询该表的所有记录。
     * SQL语句如下:select * from user;
     *
     * @param tClass 实体类,如User.class
     * @return 返回查询结果集
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    default List<T> selectAll(Class<T> tClass) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 设置查询所有记录的SQL语句
        String sql = "select * from " + tClass.getSimpleName().toLowerCase();
        // 创建存放结果的集合
        List<T> lists = new ArrayList<>();
        // 获取数据库连接并创建PreparedStatement对象,执行查询操作获取结果集
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            // 根据实体类创建实例对象
            T t = tClass.newInstance();
            // 获取t所表示的实体类的所有方法,其实主要是获取setXXX方法,设置属性的值
            Method[] methods = t.getClass().getDeclaredMethods();
            // 循环遍历方法数组
            for (Method method : methods) {
                // 过滤setXXX方法,设置属性的值
                if (method.getName().startsWith("set")) {
                    // 设置属性的值,通过rs.getObject(String columnLabel)方法获取指定列的值
                    method.invoke(t, rs.getObject(method.getName().substring(3)));
                }
            }
            // 将设置成功的实例对象添加到集合中
            lists.add(t);
        }
        return lists;
    }

}

第三步,创建对应实体类。

创建对应的实体类,该类中的属性名和对应表中的字段名一一对应,注意属性名和字段名必须完全对应,不能将下划线转换成驼峰命名。

class User {
    private Long id;
    private String username;
    private String password;
    private String phone;
    private String email;
    private String memo;
    private java.util.Date insert_time;

    public User() {
    }

    public User(String username, String password, String phone, String email, String memo, java.util.Date insert_time) {
        this.username = username;
        this.password = password;
        this.phone = phone;
        this.email = email;
        this.memo = memo;
        this.insert_time = insert_time;
    }

    public User(Long id, String username, String password, String phone, String email, String memo, java.util.Date insert_time) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.phone = phone;
        this.email = email;
        this.memo = memo;
        this.insert_time = insert_time;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getMemo() {
        return memo;
    }

    public void setMemo(String memo) {
        this.memo = memo;
    }

    public java.util.Date getInsert_time() {
        return insert_time;
    }

    public void setInsert_time(Date insert_time) {
        this.insert_time = insert_time;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\\'' +
                ", password='" + password + '\\'' +
                ", phone='" + phone + '\\'' +
                ", email='" + email + '\\'' +
                ", memo='" + memo + '\\'' +
                ", insert_time=" + insert_time +
                '}';
    }
}

第四步,创建UserMapper类实现BaseMapper<User>接口。

由于定义了默认方法,所以只需要实现BaseMapper接口,不需要自定义任何方法。

class UserMapper implements BaseMapper<User> {

}

第五步,测试方法。

class Test {
    public static void main(String[] args) throws SQLException, IllegalAccessException, InvocationTargetException, InstantiationException {
        UserMapper userMapper = new UserMapper();
        Map<String, Object> map = new HashMap<>();
        map.put("username", "张三");
        map.put("password", "123456");
        System.out.println(userMapper.selectPage(User.class, 2, 4, map));
    }
}

存在的问题

上面封装的BaseMapper<T>接口实际上是存在很多问题的,很难在实际中使用。下面列举些常见的问题:

  • 1.属性名和字段名必须一模一样。
  • 2.主键id字段的插入字段存在问题。
  • 3.conditions条件必须匹配,即至少存在一个条件。
  • 4.只能处理相等的条件,不能处理更复杂的条件查询如大于条件等。
  • 5.实体类中的属性名必须是使用引用数据类型,否则可能会引发异常。

参考链接:

以上是关于反射实例之封装对数据库的增删改查操作的主要内容,如果未能解决你的问题,请参考以下文章

Python_类和实例属性的增删改查操作

通用DAO之MyBatis封装,封装通用的增删改查

MySQL基础操作之对数据库和表的增删改查

mysql操作入门基础之对数据库和表的增删改查

python之MySQL学习——简单的增删改查封装

Python之实例对象的增删改查