使用过几个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