JDBC代码冗余问题

在上一篇文章中封装了连贯JDBC的局部,解决了连贯性能的问题。但还有一部分问题是对于代码的冗余,咱们在连贯数据库进行增删改操作的时候,大部分的代码都是一样的,惟一的不同就在于SQL语句以及给SQL语句传递的参数。和增删改操作相比,查问语句须要有返回值(返回查问的后果),而且除了SQL语句和传递的参数之外,查问语句还要处理结果集。
大部分的代码都是类似的,所以咱们须要把这部分类似的代码抽取进去。(本次的封装是基于上一篇文章的封装)

封装步骤

Sql语句的写法须要扭转:
之前:update atm set account = ? where account = ?;
扭转后:update atm set account = #{account} where account = #{account}
为什么要做扭转:如果咱们依照之前的写法,那么咱们调用办法时传递的参数必须要是有序,否则咱们没有方法断定哪个参数在前哪个参数在后。而扭转sql语句的写法之后,咱们通过解析sql语句,把#{}内的字符串解析进去,就可能很清晰的看出每个地位须要传递的参数名

  1. 首先先封装一个类SqlAndKey

     这个类的目标是为了存储被解析之后的SQL语句以及SQL语句上的参数(#{}内的字符串)
        public class SQLAndKey {        private StringBuilder sql = new StringBuilder();        //采纳List汇合是因为List汇合是有序的,可能确认参数的程序        private List<String> keyList = new ArrayList<>();        public SQLAndKey(StringBuilder sql, List<String> keyList){            this.sql = sql;            this.keyList = keyList;        }        public String getSQL(){            return sql.toString();        }        public List<String> getKeyList(){            return keyList;        }    }
  2. 封装一个Handler类

    该类的目标是为了解析Sql语句以及设置Sql语句中的参数
     public class Handler {     //==========解析SQL语句============     SQLAndKey parseSQL(String sql){         //newSql是为了拼接sql语句,keyList为了存储键值         StringBuilder newSql = new StringBuilder();         List<String> keyList = new ArrayList<>();         //解析SQL         while (true){             //查找#{和}的地位             int left = sql.indexOf("#{");             int right = sql.indexOf("}");             //判断两个符号的地位是否非法             if (left != -1 && right != -1 && left < right){                 //截取#{之前的字符串                 newSql.append(sql.substring(0, left));                 //sql前面追加?                 newSql.append("?");                 keyList.add(sql.substring(left + 2, right));             }else {                 //证实曾经没有#{和}成对呈现了                 newSql.append(sql);                 break;             }             //将}及}之前的全副截取掉             sql = sql.substring(right + 1);         }         return new SQLAndKey(newSql, keyList);     }     //==============SQL语句的拼接==================     //别离负责map 和 domain类型的拼接     private void setMap(PreparedStatement pstate, Object obj, List<String> keyList) throws SQLException {         Map map = (Map) obj;         for (int i = 0; i < keyList.size(); i ++){             pstate.setObject(i+1, map.get(keyList.get(i)));         }     }     private void setObject(PreparedStatement pstat, Object obj, List<String> keyList){         try {             //找到obj对应的类             Class clazz = obj.getClass();             for (int i = 0; i < keyList.size(); i ++){                 //找到key                 String key = keyList.get(i);                 //通过反射找到Obj对象中的属性                 Field field = clazz.getDeclaredField(key);                 field.setAccessible(true);                 //获取公有属性的值                 Object value = field.get(obj);                 pstat.setObject(i + 1, value);             }         } catch (NoSuchFieldException | IllegalAccessException e) {             e.printStackTrace();         } catch (SQLException e) {             e.printStackTrace();         }     }     //设计一个办法,负责将SQL和问号组装残缺     //参数:pstat对象,Object对象,KeyList全副的Key     void handleParameter(PreparedStatement pstat, Object obj, List<String> keyList) throws SQLException {         //1、通过反射获取obj对应的class         Class clazz = obj.getClass();         //2、判断该clazz是什么类型         if (clazz == int.class || clazz == Integer.class){             pstat.setInt(1, (Integer) obj);         }else if (clazz == float.class || clazz == Float.class){             pstat.setFloat(1, (Float) obj);         }else if (clazz == double.class || clazz == Double.class){             pstat.setString(1, (String) obj);         }else if (clazz == String.class){             pstat.setString(1, (String) obj);         }else if (clazz.isArray()){         }else {             if (obj instanceof Map){                 this.setMap(pstat, obj, keyList);             }else {                 this.setObject(pstat, obj, keyList);             }         }     }     //============通过反射获取对象===============     private Map getMap(ResultSet rs) throws SQLException {         //1、创立Map         Map<String, Object> map = new HashMap<>();         //2、获取后果集中的全副信息         ResultSetMetaData rsmd = rs.getMetaData();         //3、遍历后果集         for (int i = 1; i <= rsmd.getColumnCount(); i ++){             //获取列名             String columnName = rsmd.getColumnName(i);             //获取列值             Object value = rsmd.getColumnName(i);             //存入map中             map.put(columnName, value);         }         return map;     }     private Object getObject(ResultSet rs, Class resultType) throws SQLException {         //1、创立Object         Object obj = null;         try {             //2、通过反射创建对象             obj = resultType.newInstance();             //3、获取后果集中的全副信息             ResultSetMetaData rsmd = rs.getMetaData();             //4、遍历后果集             for (int i = 1; i <= rsmd.getColumnCount(); i ++){                 //获取列名                 String columnName = rsmd.getColumnName(i);                 //通过反射找到列名字对应的属性                 Field field = resultType.getDeclaredField(columnName);                 //操作公有属性                 field.setAccessible(true);                 //给属性赋值                 field.set(obj, rs.getObject(columnName));             }         } catch (InstantiationException e) {             e.printStackTrace();         } catch (IllegalAccessException e) {             e.printStackTrace();         } catch (NoSuchFieldException e) {             e.printStackTrace();         }         return obj;     }     public Object handleResult(ResultSet rs, Class resultType) throws SQLException {         //1、通过反射创建对象         Object result = null;         if (resultType == int.class || resultType == Integer.class){             result = rs.getInt(1);         }else if (resultType == float.class || resultType == Float.class){             result = rs.getFloat(1);         }else if (resultType == double.class || resultType == Double.class){             result = rs.getDouble(1);         } else if (resultType == String.class) {             result = rs.getString(1);         }else {             if (resultType == Map.class){                 result = this.getMap(rs);             }else {                 result = this.getObject(rs, resultType);             }         }         return result;     } }
  3. 封装增删改查的办法

    • 增删改:因为增删改操作很类似,所以咱们只须要实现更改的操作,减少和删除的操作只须要调用更改的办法。

       //改    //参数:SQL语句,传递的参数    public void update(String sql, Object obj){        try {            //1、解析sql语句            SQLAndKey sqlAndKey = handler.parseSQL(sql);            //2、获取连接池对象            ConnectionPool pool = ConnectionPool.getInstance();            //3、获取连贯对象            Connection conn = pool.getConnection();            //4、获取状态参数            PreparedStatement pstate = conn.prepareStatement(sqlAndKey.getSQL());            //5、将SQL语句和问号值组装残缺,调用handler的办法将obj对象代替掉?            if (obj != null){                handler.handleParameter(pstate, obj, sqlAndKey.getKeyList());            }            pstate.executeUpdate();            pstate.close();            conn.close();        } catch (SQLException e) {            e.printStackTrace();        }    }//删    public void delete(String sql, Object obj){this.update(sql, obj); }//增    public void insert(String sql, Object obj){this.update(sql, obj); }
    • 查找的办法:
查找的办法除了传入sql语句以及参数之外,还须要传入查问之后的返回值的类```//查问单条    public <T> T selectOne(String sql, Object obj, Class resultType){        return (T) this.selectList(sql, obj, resultType).get(0);    }    //查问多条    public <T> List<T> selectList(String sql, Object obj, Class resultType){        List<T> list = new ArrayList<>();        try {            //1、解析SQL            SQLAndKey sqlAndKey = handler.parseSQL(sql);            //2、获取连贯对象            ConnectionPool pool = ConnectionPool.getInstance();            Connection conn = pool.getConnection();            //3、获取状态参数            PreparedStatement pstat = conn.prepareStatement(sql);            //4、把SQL和问号拼接在一起            if (obj != null){                handler.handleParameter(pstat, obj, sqlAndKey.getKeyList());            }            //5、执行操作            ResultSet rs = pstat.executeQuery();            //6、处理结果            while (rs.next()){                //通过handleResult获取到                T result = (T) handler.handleResult(rs, resultType);                list.add(result);            }            rs.close();            pstat.close();            conn.close();        } catch (SQLException e) {            e.printStackTrace();        }        return list;    }```
  1. 动静代理

封装完增删改查之后,那么之后咱们在执行增删改查的时候只须要调用咱们封装的办法,例如:

 public void insert(User user){        String sql = "insert into atm values(#{account}, #{password}, #{balance})";        new SqlSession().insert(sql, user);    }

那么其实咱们能够去掉办法体的代码,只留下一个办法名。咱们通过一个代理对象来实现这些办法,咱们用SqlSession类来定义一个办法,具体的办法代码下面曾经粘贴进去了。通过这个办法咱们能够取得一个代理对象来操作理论的dao类。那么之后咱们在写增删改查的时候,只须要写sql语句,就可能实现。(实现的形式是通过注解),比方如下的代码:

public interface Dao {    @Insert("insert into atm values(#{account}, #{password}, #{balance})")    void insert(User user);    @Delete("delete from atm where account = #{account}")    void delete(String account);    @Update("update atm set account=#{account}, password=#{password}, balance=#{balance} where account=#{account}")    void update(String account);    @Select("SELECT *FROM ATM WHERE ACCOUNT = ?")    User selectOne(String account);    @Select("SELECT *FROM ATM")    List<User> selectList();}
  1. 具体调用
    当咱们须要对数据库进行操作的时候,只须要获取代理对象,调用代理对象的办法就可能实现,封装了JDBC冗余的代码,具体调用如下:

    public class TestMain {    public static void main(String[] args) {        Dao dao = new SqlSession().getMapper(Dao.class);        dao.delete("2024");    }}

因为整个封装的代码量比拟多,该篇文章也次要是为了记录本人的学习过程,所以有一些中央可能说得不到位,如果有问题都能够留言提出来

整个JDBC封装的代码:https://github.com/Cing-self/...
应用的时候记得改一下configuration.properties配置文件