興趣使然之封裝Spring的JdbcTemplate

使用過幾個ORM框架,都感受並不合適我使用,SpringData與Hibernate雖然強大,可是太多功能我平常工做或者學習上並不須要,且在調優問題上有必定的阻礙,而MyBatis又須要把sql寫到xml文件或者註解裏面,而我更喜歡sql直接寫到代碼裏面用Java代碼寫邏輯控制sql語句。就想着封裝一個簡單方便適合本身平常學習使用的ORM框架。相比直接封裝原生的JDBC,Spring提供的JdbcTemplate工具類已經提供了一條捷徑,因此此次簡單封裝一下JdbcTemplate,使平時學習能更加順手sql

先展現一下使用(下面用的都是main函數測試,由於不想引入太多依賴致使後面我學習使用時還要再刪除):數據庫

下面是上面調用的testSave()函數app

下面是上面調用的save()函數框架

save()執行完之後數據庫多了條數據而且把執行的sql打印出來了(打印用的是System.out.println打印,上圖截完才加上的),個人數據庫id是自增的,因此插入的時候沒有設置值,又把我剛封裝好時測試的數據刪除了,因此就從11開始函數

下面內容僅用於記錄封裝,不作太詳細說明工具

一,映射註解學習

ORM框架都是由對象與數據映射關係組成,爲了標明某個對象映射某個表的,對象的字段對應表的哪一個字段,因此先寫了2個註解 @Column@Table測試

@Table僅用於註明是映射哪一個表this

@Column用於註明映射哪一個字段,及是否爲主鍵spa

/**
 * 用於標識類對應數的表
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {

    String value() default "";
}
/**
 * 用於標識字段對應數的列名 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {

    String value() default "";

    boolean isKey() default false;

}

下面是使用方式,getXxx()和setXxx()函數就不復製出來了,另外最後一個test字段沒使用@Column則不進行映射關係,其餘用到註解的地方都說明了對應的是哪一個表,哪一個字段,若是是聯合主鍵則多字段使用 isKey=true

@Table("sys_user")
public class User implements Serializable {

    public final static String TABLE_USER="sys_user";

    @Column(value = "id",isKey = true)
    private Long id;

    @Column("user_name")
    private String userName;

    @Column("password")
    private String password;

    @Column("nick_name")
    private String nickName;

    @Column("gender")
    private Integer gender;

    @Column("mobile")
    private String mobile;

    @Column("email")
    private String email;

    private String test;

二,根據映射對象生成DML語句

編寫了一個DaoUtils的泛型類,這樣就能夠根據各類對象動態生成sql

裏面有13個函數(一個main方法,前期測試使用),1個屬性

table屬性:主要用於存儲經過analysisTable(T t)函數反射獲取到的表信息與字段信息

/**
 * 存表信息字段信息
 */
private Map<String,Object> table;

analysisTable(T t):經過反射獲取到的表信息和字段信息

下面是table屬性儲存的屬性

tableName:存儲表名

tableColumns:儲存表字段信息

keyColumns:儲存主鍵信息

/**
 * 根據取表信息,表名,字段名,字段 * @return
 * @throws Exception
 */
private Map<String,Object> analysisTable(T t) throws DtoAnalysisTableException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class<?> clazz = t.getClass();
    // 象全部的信息集合,表,字段,字段    Map<String,Object> result = new HashMap<String,Object>();
    // 存字段信息
    Map<String,Object> fieldMap = new TreeMap<String, Object>();
    // 存主信息
    Map<String,Object> keyMap = new TreeMap<String, Object>();
    // 名,Table註解
    Table table = clazz.getAnnotation(Table.class);
    if(table == null) {
        throw new DtoAnalysisTableException("Object not has Table of Annotation");
    }
    // 取全部字段名    Field[] fields = clazz.getDeclaredFields();
    // 字段字段的信息
    for (Field field: fields) {
        // 取字段映射信息
        Column annotation = field.getAnnotation(Column.class);
        // 存在映射信息候才        if(annotation != null){
            // getXxx()            Method getMethod = clazz.getDeclaredMethod(MethodUtils.getMethod(field.getName()));
            // 根據getXxx數獲            Object invoke = getMethod.invoke(t);
            // 該數字段對應            fieldMap.put(annotation.value(), invoke);
            // 是否主,若是是到主信息
            if(annotation.isKey()){
                keyMap.put(annotation.value(),invoke);
            }
        }
    }
    // 添加表名
    result.put("tableName", table.value());
    // 添加字段信息
    result.put("tableColumns", fieldMap);
    // 添加主字段信息
    result.put("keyColumns",keyMap);
    return result;
}

上面用到的自定義類 MethodUtils 與自定義異常 DtoAnalysisTableException

MethodUtils 主要提供了根據字段名獲取getXxx()和setXxx()的函數

/**
 * 入一字段名,字段的
 * getXxx()
 * @param fieldName
 * @return
 */
public static String getMethod(String fieldName){
    if(fieldName == null || fieldName.length() < 1){
        return "";
    }
    return "get"+fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
/**
 * 入一字段名,字段的
 * setXxx()
 * @param fieldName
 * @return
 */
public static String setMethod(String fieldName){
    if(fieldName == null || fieldName.length() < 1){
        return "";
    }
    return "set"+fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}

DtoAnalysisTableException這是該類沒有表映射關係的異常

/**
 * 映射 */
public class DtoAnalysisTableException extends Exception{

    public DtoAnalysisTableException(String message) {
        super(message);
    }

}

接着說回DaoUtils的函數

getTable(T t) 是獲取 table的函數,避免同一DaoUtils對象屢次調用analysisTable(T t)浪費資源

/**
 * 用於映射的表信息
 * @param t
 * @return
 */
private Map<String, Object> getTable(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    if(table == null) {
        table = analysisTable(t);
    }
    return table;
}

insertCols() 與 updateCols()則是獲取的 sql 的部分語句 例以下面圖片紅圈出:

/**
 * 根據getTable()返回的集合 tableColumns 取持久化象的性名字符串
 * @param colMap
 * @return
 */
private String insertCols(Map<String,Object> colMap){
    StringBuffer insertCols = new StringBuffer("");
    // key(表字段名)
    Set<String> keySet = colMap.keySet();
    // key對應接部分sql句,例如:" fieldName1,fieldName2,fieldName3 "
    for (String col : keySet) {
        if(insertCols.length() > 0){
            insertCols.append(",");
        }
        insertCols.append(col);
    }
    return insertCols.toString();
}

/**
 * 根據getTable()返回的集合 tableColumns 取持久化象的性名字符串
 * @param colMap
 * @return
 */
private String updateCols(Map<String,Object> colMap){
    StringBuffer insertCols = new StringBuffer("");
    // key(表字段名)
    Set<String> keySet = colMap.keySet();
    // key對應接部分sql句,例如:"fieldName1=?,fieldName2=?,fieldName3=? "
    for (String col : keySet) {
        if(insertCols.length() > 0){
            insertCols.append(",");
        }
        insertCols.append(col).append("=?");
    }
    return insertCols.toString();
}

下面的 insertSql(T t) insertBatchSql(T t,int size)updateSql(T t)deleteSql(T t) 則是獲取插入、批量插入、根據主鍵修改、根據主鍵刪除的SQL

/**
 * 根據表信息,insert sql * @return
 */
public String insertSql(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 反射    Map<String, Object> tableByDto = getTable(t);
    // 取表名
    String tableName = tableByDto.get("tableName")+"";
    // 取表字段
    Map<String, Object> colMap = (Map<String, Object>) tableByDto.get("tableColumns");
    // sql
    StringBuffer sql = new StringBuffer(" INSERT INTO ");
    //  "" 這樣並拼    sql.append(tableName).append(" (").append(insertCols(colMap)).append(") ");
    //  "?,?,?,?,?" 這樣並拼    sql.append(" VALUES ").append(" (").append(insertParam(colMap)).append(") ");
    return sql.toString();
}

/**
 * 根據表信息,批量insert sql * @param size 批量的 * @return
 */
public String insertBatchSql(T t,int size) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 反射    Map<String, Object> tableByDto = getTable(t);
    // 取表字段
    Map<String, Object> colMap = (Map<String, Object>) tableByDto.get("tableColumns");
    StringBuffer sql = new StringBuffer(insertSql(t));
    //  "?,?,?,?,?" 這樣    String insertParam = insertParam(colMap);
    // sql
    for (int i = 0 ; i < size - 1; i++){
        sql.append(" , (").append(insertParam).append(") ");
    }
    return sql.toString();
}

/**
 * 根據表信息,update sql * @return
 */
public String updateSql(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 反射    Map<String, Object> tableByDto = getTable(t);
    // 取表字段
    Map<String, Object> colMap = (Map<String, Object>) tableByDto.get("tableColumns");
    // 取表主    Map<String, Object> keyMap = (Map<String, Object>) tableByDto.get("keyColumns");
    //     String tableName = tableByDto.get("tableName")+"";
    Set<String> keys = keyMap.keySet();
    // UPDATE SQL
    StringBuffer sql = new StringBuffer(" UPDATE ");
    sql.append(tableName).append(" set ").append(updateCols(colMap)).append(" ");
    sql.append(" where ");
    int i = 0;
    for (String key : keys) {
        sql.append(key).append("=? ").append((i++ < keys.size()-1) ? " and " : "");
    }
    return sql.toString();
}


/**
 * 根據表信息,取根據主delete sql * @return
 */
public String deleteSql(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 反射    Map<String, Object> tableByDto = getTable(t);
    // 取表名
    String tableName = tableByDto.get("tableName")+"";
    // 取主    Map<String, Object> keyMap = (Map<String, Object>) tableByDto.get("keyColumns");
    Set<String> keys = keyMap.keySet();
    // sql
    StringBuffer sql = new StringBuffer(" DELETE from ");
    sql.append(tableName).append(" WHERE ");
    int i = 0;
    for (String key : keys) {
        sql.append(key).append("=? ").append((i++ < keys.size()-1) ? " and " : "");
    }
    return sql.toString();
}

上面有使用到 insertParam() 函數 主要是用於獲取插入語句的 「?,?,?,?」 這樣的內容

/**
 * 參數字符串 格式:?,?,?,?
 * @param colMap
 * @return
 */
private String insertParam(Map<String,Object> colMap){
    StringBuffer param = new StringBuffer("");
    for (int i = 0;i < colMap.size(); i++){
        param.append("?");
        if(i < colMap.size()-1){
            param.append(",");
        }
    }
    return param.toString();
}

再接下來是獲取使JdbcTemplate接口時候須要使用的參數值。insertValues(T t)insertBatchValues(List<T> list)updateValues(T t)deleteValues(T t) 對應的分別是獲取插入、批量插入、根據主鍵修改和根據主鍵刪除的函數

/**
 * 根據getTable()返回的集合 tableColumns 象的 * @param t
 * @return
 */
public Object[] insertValues(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 取表信息
    Map<String, Object> table = getTable(t);
    // 取表字段
    Map<String, Object> colMap = (Map<String, Object>) table.get("tableColumns");
    Object[] values = new Object[colMap.size()];
    // 根據字段名    Set<String> keySet = colMap.keySet();
    int i = 0;
    for (String col : keySet) {
        values[i++] = colMap.get(col);
    }
    return values;
}


/**
 * 根據getTable()返回的集合 tableColumns 象的 * @return
 */
public Object[] insertBatchValues(List<T> list) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 用於存全部參數
    List<Object> values = new ArrayList<Object>();
    // 參數
    for (int i = 0;i < list.size(); i++) {
        // 調insertValues該對象的參數值
        Object[] objects = insertValues(list.get(i));
        // 參數添加到列表
        values.addAll(Arrays.asList(objects));
    }
    return values.toArray();
}

/**
 * 根據getTable()返回的集合 tableColumns 象的 * @param t
 * @return
 */
public Object[] updateValues(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 取表信息
    Map<String, Object> table = getTable(t);
    // 取全部表字段
    Map<String, Object> colMap = (Map<String, Object>) table.get("tableColumns");
    // 取主字段
    Map<String, Object> keyMap = (Map<String, Object>) table.get("keyColumns");
    List<Object> values = new ArrayList<Object>();
    // 取全部字段
    Set<String> keySet = colMap.keySet();
    // 根據key對應    for (String col : keySet) {
        values.add(colMap.get(col));
    }
    // 取主字段
    Set<String> keys = keyMap.keySet();
    // 根據key對應    for (String key : keys) {
        values.add(colMap.get(key));
    }
    return values.toArray();
}


/**
 * 根據getTable()返回的集合 tableColumns 象的 * @param t
 * @return
 */
public Object[] deleteValues(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {
    // 取表信息
    Map<String, Object> table = getTable(t);
    // 取字段信息
    Map<String, Object> colMap = (Map<String, Object>) table.get("tableColumns");
    // 取主信息
    Map<String, Object> keyMap = (Map<String, Object>) table.get("keyColumns");
    // key(表性)
    Set<String> keys = keyMap.keySet();
    List<Object> values = new ArrayList<Object>();
    // 根據key對應    for (String key : keys) {
        values.add(colMap.get(key));
    }
    return values.toArray();
}

對於這個類獲取SQL的測試,使用main()調用

// 測試使用
public static void main(String[] args) {
    try{
        User user = new User();
        user.setUserName("11111");
        user.setPassword("fdsfds");
        user.setMobile("137");
        user.setEmail("email");
        // 添加sql
        String insertSql = new DaoUtils<User>().insertSql(user);
        System.out.println(insertSql);
        // 修改sql
        String updateSql = new DaoUtils<User>().updateSql(user);
        System.out.println(updateSql);
        // 修改sql
        String deleteSql = new DaoUtils<User>().deleteSql(user);
        System.out.println(deleteSql);
        // 批量添加sql
        String insertBatchSql = new DaoUtils<User>().insertBatchSql(user,3);
        System.out.println(insertBatchSql);
    } catch (Exception e){
        e.printStackTrace();
    }

}

打印結果:

下面就是封裝好的 BaseDao

public class BaseDao<T> {

    protected JdbcTemplate jdbcTemplate;

    /**
     * 入函     * @param t
     */
    public void save(T t) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {
        // Util        DaoUtils<T> du = new DaoUtils<T>();
        // sql        String s = du.insertSql(t);
        // sql參數
        Object[] params = du.insertValues(t);
        // 調JdbcTemplate         jdbcTemplate.update(s,params);
    }

    /**
     * 修改函     * @param t
     */
    public void update(T t) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {
        // Util        DaoUtils<T> du = new DaoUtils<T>();
        // sql        String s = du.updateSql(t);
        // sql參數
        Object[] params = du.updateValues(t);
        // 調JdbcTemplate         jdbcTemplate.update(s,params);
    }

    /**
     * 除函     * @param t
     */
    public void delete(T t) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {
        // Util        DaoUtils<T> du = new DaoUtils<T>();
        // sql        String s = du.deleteSql(t);
        // sql參數
        Object[] params = du.deleteValues(t);
        // 調JdbcTemplate         jdbcTemplate.update(s,params);
    }

    /**
     * 批量入函     * @param list
     */
    public void batchSave(List<T> list) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {
        // Util        DaoUtils<T> du = new DaoUtils<T>();
        // sql        String s = du.insertBatchSql(list.get(0), list.size());
        // sql參數
        Object[] params = du.insertBatchValues(list);
        // 調JdbcTemplate         jdbcTemplate.update(s,params);
    }

    // 測試    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

固然,最開始只是由於測試,因此直接使用的BaseDao,實際寫一個UserDao再繼承BaseDao使用更方便,這樣可使用增刪改的工具函數,也能定義屬於User類本身的sql例如:

public class UserDao extends BaseDao<User> {

    public List<User> list(){
        return jdbcTemplate.query(" select * from " + User.TABLE_USER , new Object[]{},new LocalRowMapper<User>(User.class));
    }

}

上面BaseDao的增刪改工具函數就再也不進行測試了

三,下面開始說說查詢

JdbcTemplate 原生的query()是不支持類字段和表字段映射規則不同的查詢操做,例如:數據庫字段 user_name 與 類屬性 userName 這2個能夠映射到,query時會根據下劃線"_"的位置進行駝峯命名,可是若是遇到 user_name 與 userName001 這樣的字段,則不能經過映射,下面測試一下

這是表的內容

先測試用JdbcTemplate提供的 BeanPropertyRowMapper 執行

查詢list函數

測試函數

main()

看看執行內容:證實若是是user_name和userName可以映射

再把咱們上面的User類代碼的userName改爲userName1,

main()執行結果,能夠看到userName1的字段是null,並無映射到:

下面再改爲我本身重寫RowMapper後的 LocalRowMapper,其餘代碼不變,把BeanPropertyRowMapper 改爲 LocalRowMapper

執行結果,換成了LocalRowMapper以後userName1與user_name也能映射到,userName與user_name更不用說了:

userName1

userName

下面看看 LocalRowMapper 類

/**
 * RowMapper,使使用@Table @Column
 * @param <T>
 */
public class LocalRowMapper<T> implements RowMapper<T>{

   private Class<?> clazz;

   // 保存字段與數字段的映射   private Map<String,String> fieldMap;

   // 保存字段setXxx()參數類   private Map<String,Class> fieldTypeMap;
   
   public LocalRowMapper(Class<?> clazz){
      try {
         // calss
         this.clazz = clazz;
         // 初始化 fieldMap
         this.fieldMap = new HashMap<String, String>();
         // 初始化 fieldTypeMap
         this.fieldTypeMap = new HashMap<String, Class>();
         this.analysisTable();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }


   /**
    * RowMapper mapRow()     * @param rs
    * @param arg
    * @return
    */
   public T mapRow(ResultSet rs, int arg) {
      T t= null;
      try {
         // 根據class 例化         t = (T)clazz.newInstance();
         // 與數映射的字段
         Set<String> keySet = this.fieldMap.keySet();
         for (String key: keySet) {
            // setXxx()
            Method setMethod = clazz.getDeclaredMethod(MethodUtils.setMethod(key),this.fieldTypeMap.get(key));
            // 根據字段名稱獲ResultSet返回字段所在下            // 標獲取到於字段的            // setXxx() 值設置到            setMethod.invoke(t,rs.getObject(rs.findColumn(this.fieldMap.get(key))));
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
      return t;
   }

   /**
    * 取字段映射信息函    * @throws DtoAnalysisTableException
    * @throws NoSuchMethodException
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    */
   private void analysisTable() throws DtoAnalysisTableException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
      Class<?> clazz = this.clazz;
      // 取全部字段名      Field[] fields = clazz.getDeclaredFields();
      // 字段字段的信息
      for (Field field: fields) {
         Column annotation = field.getAnnotation(Column.class);
         if(annotation != null){
            // 保存字段與數字段
            this.fieldMap.put(field.getName(), annotation.value());
            // 保存字段setXxx()參數類            this.fieldTypeMap.put(field.getName(),field.getType());
         }
      }
   }

}

以上就是對此次 JdbcTemplate裝的全部內容了,下面是源碼連接(OSCHINA不能上傳附件只能放雲盤了)

https://pan.baidu.com/s/1e0DsZKP_D9dWVcZ4TRayHQ  

提取碼:awip

相關文章
相關標籤/搜索