• 144802

    文章

  • 854

    评论

  • 13

    友链

  • 最近新加了换肤功能,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

兴趣使然之封装Spring的JdbcTemplate


使用过几个ORM框架,都感觉并不合适我使用,SpringData与Hibernate虽然强大,但是太多功能我日常工作或者学习上并不需要,且在调优问题上有一定的阻碍,而MyBatis又需要把sql写到xml文件或者注解里面,而我更喜欢sql直接写到代码里面用Java代码写逻辑控制sql语句。就想着封装一个简单方便适合自己日常学习使用的ORM框架。相比直接封装原生的JDBC,Spring提供的JdbcTemplate工具类已经提供了一条捷径,所以这次简单封装一下JdbcTemplate,使平时学习能更加顺手

先展示一下使用(下面用的都是main函数测试,因为不想引入太多依赖导致后面我学习使用时还要再删除):

下面是上面调用的testSave()函数

下面是上面调用的save()函数

save()执行完以后数据库多了条数据并且把执行的sql打印出来了(打印用的是System.out.println打印,上图截完才加上的),我的数据库id是自增的,所以插入的时候没有设置值,又把我刚封装好时测试的数据删除了,所以就从11开始

下面内容仅用于记录封装,不做太详细说明

一,映射注解

ORM框架都是由对象与数据映射关系组成,为了标明某个对象映射某个表的,对象的字段对应表的哪个字段,所以先写了2个注解 @Column@Table

@Table仅用于注明是映射哪个表

@Column用于注明映射哪个字段,及是否为主键

/**
 * 用于标识类对应数的表
 */
@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

 

 


695856371Web网页设计师②群 | 喜欢本站的朋友可以收藏本站,或者加入我们大家一起来交流技术!

0条评论

Loading...


发表评论

电子邮件地址不会被公开。 必填项已用*标注

自定义皮肤 主体内容背景
打开支付宝扫码付款购买视频教程
遇到问题联系客服QQ:419400980
注册梁钟霖个人博客