栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

学习记录01——使用原生jdbc时出现的一些重复代码

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

学习记录01——使用原生jdbc时出现的一些重复代码

  最近在复习jdbc一些东西,感觉用多了spring全家桶之后只知道怎么使用它封装好的注解,却忘了一些基本的东西,好像变成了spring程序员而不是java程序员了,然而在复习jdbc时发现有些经常重复的操作没法省略,所以想了个办法把它简化,我知道现在市面上使用的如JdbcTemplate和Mybatis的工具都能很好的解决这些问题,但我就是想自己写一个简单的工具,通过反射和泛型去解决这一麻烦。当然了,实际开发中肯定还是用mybatis跟mp,这个解决方法只不过是一个小实践,尝试去手写一个简易版JdbcTemplate而已。

  先来看看原生jdbc的操作:

public class TestJDBC {

    Connection connection;

    @Before
    public void before(){
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql:///db_test","root", "root");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testJdbc() throws SQLException {
        PreparedStatement ps = connection.prepareStatement("select * from t_user where id = ?");
        ps.setString(1,"1");
        //...有多少个占位符set多少次

        //dql用executeQuery,dml用execute
        ResultSet rs = ps.executeQuery();//ps.execute
        User user = new User();
        while (rs.next()){
            user.setId(rs.getLong("id"));
            user.setUsername(rs.getString("username"));
            //...有多少个字段就要set多少次,同理rs就要get多少次
        }
        //return user;
    }

}

 里面会出现大量的ps.setxxx方法和执行dql时操作rs的重复代码,并且当我要获取另一个实体类时,又要重新写一个方法去set属性又rs.get属性,所以我写了一个类似于JdbcTemplate的东西,可以在查询单条数据和多条数据时复用。

  首先是写了一个方法能通过反射获取该类所有成员变量,并找到他的set属性,再通过成员变量名找到结果集中对应字段给实体类附上值,当然,这样子的话数据库字段名必须跟实体类的成员变量名一致,否则会赋值失败。

  这里稍微说下获取set属性的方式,个人感觉还有其他更好的方法,毕竟getDeclaredMethods会把equals、toString等方法一起获取,减慢了效率。但是没有去深究。

private  T autoInject(Class clazz,ResultSet rs,Object instance) throws SQLException, InvocationTargetException, IllegalAccessException {

        //反射获取所有声明的成员变量
        Field[] declaredFields = clazz.getDeclaredFields();

        //反射获取所有声明的get,set方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Field field : declaredFields) {
            String fieldName = field.getName();
            Class type = field.getType();
            Date date = null;
            Object object= null;
            //获取日期时调用getDate方法,否则后面强转Date会失败,我也不知道为什么
            if (type == Date.class){
                date = rs.getDate(fieldName);
            }else {
                //其他情况调用getObject方法就行
                object = rs.getObject(fieldName);
            }

            for (Method method : declaredMethods) {
                String methodNameWithoutGS = method.getName().substring(3);
                String methodNameOnlyGS = method.getName().substring(0, 3);
                //如果方法名去掉get和set三个字母后的名字和成员变量名相同,调用set方法为泛型类赋值,并排除get方法
                if (methodNameWithoutGS.equalsIgnoreCase(fieldName) && !methodNameOnlyGS.equalsIgnoreCase("get")){
                    if (type == Date.class){
                        method.invoke(instance,date);
                        break;
                    }
                    method.invoke(instance,object);
                    break;
                }
            }
        }
        return (T) instance;
    }

通过这个方法,就可以为实体类中的所有属性赋值,当然还是那句话,rs(结果集)中的字段名必须跟成员变量名一致。

接下来需要解决ps.setxxx的这个问题,这里通过for循环给它赋值就好了

private PreparedStatement getPS(String sql,Object ...params) throws SQLException {
        connection = dataSource.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        //填充占位符
        for (int i = 0; i < params.length; i++) {
            ps.setObject(i+1,params[i]);
        }
        return ps;
    }

最后把他放到查询方法中,这里只写了dql了,dml由于没有结果集的操作,会方便很多。

查询一条数据的通用方法:

public  T queryOne(String sql,Class clazz,Object ...params) throws Exception{

        PreparedStatement ps = getPS(sql, params);
        ResultSet rs = ps.executeQuery();
        try {

            //因为是查询一个结果,返回的对象造外面,如果结果集有多个,仅返回最后一个结果
            T emptyInstance = clazz.getDeclaredConstructor().newInstance();

            while (rs.next()){

                T injectedInstance = autoInject(clazz, rs, emptyInstance);

                return injectedInstance;
            }
        } finally {
            closeResource(rs,ps);
        }

        return null;//查不到结果
    }

查询多个结果的:

 public  List queryLots(String sql,Class clazz,Object ...params) throws Exception{
            List list = new ArrayList<>();
            PreparedStatement ps = getPS(sql, params);
            ResultSet rs = ps.executeQuery();

        try {
            while (rs.next()){
                T emptyInstance = clazz.getDeclaredConstructor().newInstance();

                T injectedInstance = autoInject(clazz, rs, emptyInstance);

                list.add(injectedInstance);
            }

            return list;
        } finally {
            closeResource(rs,ps);
        }
    }

  虽然跟JdbcTemplate相比,这些功能还有很多不完善或者会出bug的地方,但是感觉自己通过反射和泛型解决掉使用原生jdbc会出现的重复操作感觉还是很好的,虽然没什么实际用处就是了。。

整个类完整代码如下:

public abstract class BaseDao {

    protected static Connection connection;
    protected static DataSource dataSource;

    static {
        try (
                InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("druid.properties");
                ){
            Properties prop = new Properties();
            prop.load(is);

            dataSource = DruidDataSourceFactory.createDataSource(prop);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public  T queryOne(String sql,Class clazz,Object ...params) throws Exception{

        PreparedStatement ps = getPS(sql, params);
        ResultSet rs = ps.executeQuery();
        try {

            //因为是查询一个结果,返回的对象造外面,如果结果集有多个,仅返回最后一个结果
            T emptyInstance = clazz.getDeclaredConstructor().newInstance();

            while (rs.next()){

                T injectedInstance = autoInject(clazz, rs, emptyInstance);

                return injectedInstance;
            }
        } finally {
            closeResource(rs,ps);
        }

        return null;//查不到结果
    }

    public  List queryLots(String sql,Class clazz,Object ...params) throws Exception{
            List list = new ArrayList<>();
            PreparedStatement ps = getPS(sql, params);
            ResultSet rs = ps.executeQuery();

        try {
            while (rs.next()){
                T emptyInstance = clazz.getDeclaredConstructor().newInstance();

                T injectedInstance = autoInject(clazz, rs, emptyInstance);

                list.add(injectedInstance);
            }

            return list;
        } finally {
            closeResource(rs,ps);
        }
    }

    private PreparedStatement getPS(String sql,Object ...params) throws SQLException {
        connection = dataSource.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        //填充占位符
        for (int i = 0; i < params.length; i++) {
            ps.setObject(i+1,params[i]);
        }
        return ps;
    }

    private  T autoInject(Class clazz,ResultSet rs,Object instance) throws SQLException, InvocationTargetException, IllegalAccessException {

        //反射获取所有声明的成员变量
        Field[] declaredFields = clazz.getDeclaredFields();

        //反射获取所有声明的get,set方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Field field : declaredFields) {
            String fieldName = field.getName();
            Class type = field.getType();
            Date date = null;
            Object object= null;
            //获取日期时调用getDate方法,否则后面强转Date会失败,我也不知道为什么
            if (type == Date.class){
                date = rs.getDate(fieldName);
            }else {
                //其他情况调用getObject方法就行
                object = rs.getObject(fieldName);
            }

            for (Method method : declaredMethods) {
                String methodNameWithoutGS = method.getName().substring(3);
                String methodNameOnlyGS = method.getName().substring(0, 3);
                //如果方法名去掉get和set三个字母后的名字和成员变量名相同,调用set方法为泛型类赋值,并排除get方法
                if (methodNameWithoutGS.equalsIgnoreCase(fieldName) && !methodNameOnlyGS.equalsIgnoreCase("get")){
                    if (type == Date.class){
                        method.invoke(instance,date);
                        break;
                    }
                    method.invoke(instance,object);
                    break;
                }
            }
        }
        return (T) instance;
    }

    public void closeResource(ResultSet rs,PreparedStatement ps){
        try {
            if (connection != null)
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (rs != null)
                rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (ps != null)
                ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void closeResource(PreparedStatement ps){
        closeResource(null,ps);
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/939812.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号