动态SQL数据层框架(3):BaseDao接口和BaseDaoSupport抽象类
这是一个简单的数据层框架,可以实现动态SQL查询、自动化事务管理、IOC和依赖注入,使用了以下技术:
1) Maven管理依赖,Github托管代码 2) DBUtils框架作为JDBC底层框架 3) JDK动态代理实现的AOP 4) 注解 5) Freemarker来做动态SQL模板的解析 6) 工厂设计模式 7) 反射和XML解析
从使用方式开始介绍,逐步深入地去理解框架的各个知识点。
框架主要分为两部分:
1) dynamic-sql-dao是框架主体
2) dynamic-sql-dao-test是一个demo含有测试用例
Github地址
https://github.com/xuguofeng/dynamic-sql-dao
https://github.com/xuguofeng/dynamic-sql-dao-test
文章目录
动态SQL数据层框架(0):一个基于FreeMarker和DBUtils的动态SQL框架
动态SQL数据层框架(1):DBUtils框架基础
动态SQL数据层框架(2):框架结构
动态SQL数据层框架(3):BaseDao接口和BaseDaoSupport抽象类
动态SQL数据层框架(4):DynamicSQLParser工具类和配置文件
动态SQL数据层框架(5):AOP事务管理
BaseDao接口用于定义最基础的数据操作的方法。BaseDaoSupport抽象类对BaseDao中的方法做了实现。本文将作详细介绍
一、BaseDao接口介绍
首先,这是一个泛型接口,实现类需要指定泛型的具体类型。
此接口定义了实体操作的通用增删改查方法
插入实体数据到数据库并返回插入数据的主键
int addObject(E e);
删除指定主键的数据并返回操作是否成功
boolean deleteObject(Integer id);
修改指定实体数据并返回操作是否成功
boolean updateObject(E e);
根据主键获取一个实体对象
E getObject(Integer id);
根据动态查询条件获取一个实体对象
E getObject(Map<String, Object> paramaters);
获取全部实体对象的集合
List<E> getObjects();
根据动态查询条件获取实体对象的集合
List<E> getObjects(Map<String, Object> paramaters);
根据动态查询条件获取分页实体对象的集合
List<E> getObjects(Map<String, Object> paramaters, Integer pageNum, Integer pageSize);
根据动态查询条件获取实体对象的数量
int count(Map<String, Object> paramaters);
二、BaseDaoSupport抽象类详解
该类实现了BaseDao接口。使用DBUtils进行JDBC操作,使用DynamicSQLParser进行动态SQL解析。
注意:在使用DynamicSQLParser获取SQL时传入的id需要和sql配置文件中<sql>标签的id属性一致。
1、核心字段
1 // 用于动态SQL解析 2 private DynamicSQLParser sqlParser = DynamicSQLParser.getDynamicSQLParser(); 3 4 // 匹配SQL中的#arg# 5 private static Pattern argPattern = Pattern.compile("#([^\\s]+)#"); 6 7 // 泛型类型 8 protected Class<E> entityClass = GenericsUtil.getSuperClassGenricType(getClass());
2、增删改方法
addObject方法
首先使用DynamicSQLParser获取SQL(不进行动态解析,所以SQL中不要含有动态标签),然后使用反射技术从参数对象中获取需要注入到SQL中参数,并封装为集合,最后执行SQL。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 QueryRunner qr = new QueryRunner(); 2 try { 3 4 Connection conn = TransactionManager.getConnection(); 5 6 // 获取插入数据类型的简单名 7 String clazzName = this.entityClass.getSimpleName(); 8 9 // 保存全部注入参数 10 List<Object> l = new ArrayList<Object>(); 11 12 // 获取SQL模板 13 // SQL的key为:数据类型简单名 拼上 _addObject 14 // 不需要进行动态SQL解析,所以后两个参数分别为null、false 15 String sql = this.sqlParser.parseSql(clazzName + "_addObject", null, false); 16 17 // 匹配SQL中全部待赋值的参数 18 Matcher m = argPattern.matcher(sql); 19 while (m.find()) { 20 String arg = m.group(1); 21 try { 22 PropertyDescriptor pd = new PropertyDescriptor(arg, this.entityClass); 23 Method getter = pd.getReadMethod(); 24 Object ret = getter.invoke(e); 25 l.add(ret); 26 } catch (Exception e1) {} 27 } 28 // 替换占位符 29 sql = sql.replaceAll("#([^\\s]+)#", "?"); 30 // 执行SQL获取id 31 return qr 32 .insert(conn, sql, new ScalarHandler<Long>(1), l.toArray()) 33 .intValue(); 34 } catch (SQLException e1) { 35 throw new RuntimeException(e1.getMessage(), e1); 36 }
deleteObject方法
首先使用DynamicSQLParser获取SQL(不进行动态解析,所以SQL中不要含有动态标签),传入id参数后即执行SQL。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 QueryRunner qr = new QueryRunner(); 2 try { 3 Connection conn = TransactionManager.getConnection(); 4 // 获取插入数据类型的简单名 5 String clazzName = this.entityClass.getSimpleName(); 6 7 // 获取SQL模板 8 // SQL的key为:数据类型简单名 拼上 _deleteObject 9 // 不需要进行动态SQL解析,所以后两个参数分别为null、false 10 String sql = this.sqlParser.parseSql(clazzName + "_deleteObject", null, false); 11 12 // 执行SQL获取影响的行数 13 int rows = qr.update(conn, sql, id); 14 return rows == 1; 15 } catch (SQLException e1) { 16 throw new RuntimeException(e1.getMessage(), e1); 17 }
updateObject方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 QueryRunner qr = new QueryRunner(); 2 try { 3 Connection conn = TransactionManager.getConnection(); 4 5 // 获取插入数据类型的简单名 6 String clazzName = this.entityClass.getSimpleName(); 7 8 // 保存全部注入参数 9 List<Object> l = new ArrayList<Object>(); 10 11 // 获取SQL模板 12 // SQL的key为:数据类型简单名 拼上 _updateObject 13 // 不需要进行动态SQL解析,所以后两个参数分别为null、false 14 String sql = this.sqlParser.parseSql(clazzName + "_updateObject", null, false); 15 // 匹配SQL中待赋值的参数 16 Matcher m = argPattern.matcher(sql); 17 while (m.find()) { 18 String arg = m.group(1); 19 try { 20 PropertyDescriptor pd = new PropertyDescriptor(arg, this.entityClass); 21 Method getter = pd.getReadMethod(); 22 Object ret = getter.invoke(e); 23 l.add(ret); 24 } catch (Exception e1) {} 25 } 26 // 替换占位符 27 sql = sql.replaceAll("#([^\\s]+)#", "?"); 28 // 执行SQL获取影响的行数 29 int rows = qr.update(conn, sql, l.toArray()); 30 return rows == 1; 31 } catch (SQLException e1) { 32 throw new RuntimeException(e1.getMessage(), e1); 33 }
3、查询方法
getObject(int)方法
首先使用DynamicSQLParser获取SQL(不需要动态解析),然后获取ResultSetHandler,子类可以重写getResultSetHandler()进行自定义,传入id参数后执行SQL并返回结果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 QueryRunner qr = new QueryRunner(); 2 try { 3 Connection conn = TransactionManager.getConnection(); 4 // 获取插入数据类型的简单名 5 String clazzName = this.entityClass.getSimpleName(); 6 // 获取SQL模板 7 // SQL的key为:数据类型简单名 拼上 _getObject_int 8 // 不需要进行动态SQL解析,所以后两个参数分别为null、false 9 String sql = this.sqlParser.parseSql(clazzName + "_getObject_int", null, false); 10 // 执行SQL获取返回对象 11 ResultSetHandler<E> rsh = getResultSetHandler(); 12 E e = (E) qr.query(conn, sql, rsh, id); 13 return e; 14 } catch (SQLException e1) { 15 throw new RuntimeException(e1.getMessage(), e1); 16 }
getObject(Map)方法
首先使用DynamicSQLParser获取SQL(需要根据Map条件进行动态解析),然后从Map中获取注入到SQL中的参数,并封装为集合,获取ResultSetHandler,子类可以重写getResultSetHandler()进行自定义,最后执行SQL并返回结果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 QueryRunner qr = new QueryRunner(); 2 try { 3 Connection conn = TransactionManager.getConnection(); 4 5 // 获取插入数据类型的简单名 6 String clazzName = this.entityClass.getSimpleName(); 7 8 // 保存全部注入参数 9 List<Object> l = new ArrayList<Object>(); 10 11 // 获取SQL模板 12 // SQL的key为:数据类型简单名 拼上 _getObject_map 13 String sql = this.sqlParser.parseSql(clazzName + "_getObject_map", paramaters, true); 14 15 // 匹配SQL中待赋值的参数 16 Matcher m = argPattern.matcher(sql); 17 while (m.find()) { 18 String arg = m.group(1); 19 Object val = paramaters.get(arg); 20 if (val instanceof List) { 21 StringBuilder sb = new StringBuilder(); 22 List v = (List) val; 23 int iMax = v.size() - 1; 24 for (int i = 0;; i++) { 25 sb.append("?"); 26 l.add(v.get(i)); 27 if (i == iMax) { 28 break; 29 } 30 sb.append(", "); 31 } 32 sql = sql.replace("#" + arg + "#", sb.toString()); 33 } else { 34 l.add(val); 35 } 36 } 37 // 替换占位符 38 sql = sql.replaceAll("#([^\\s]+)#", "?"); 39 ResultSetHandler<E> rsh = getResultSetHandler(); 40 // 执行SQL获取返回对象 41 E e = (E) qr.query(conn, sql, rsh, l.toArray()); 42 return e; 43 } catch (SQLException e1) { 44 throw new RuntimeException(e1.getMessage(), e1); 45 }
4、其他方法
execute(String sqlId, Object... args)方法
根据指定sqlId从模板中获取SQL执行,此方法不支持动态SQL解析。通常用于执行一些维护关联关系的添加、更新和删除语句。
1 protected boolean execute(String sqlId, Object... args) { 2 QueryRunner qr = new QueryRunner(); 3 try { 4 Connection conn = TransactionManager.getConnection(); 5 // 获取SQL模板 6 String sql = this.sqlParser.parseSql(sqlId, null, false); 7 return qr.update(conn, sql, args) > 0; 8 } catch (SQLException e1) { 9 throw new RuntimeException(e1.getMessage(), e1); 10 } 11 }
selectColumn(String sqlId, Map<String, Object> paramaters)方法
根据指定sqlId从模板中获取SQL,使用Map为查询条件赋值,返回某一列数据的集合。通常可以用于执行获取count、sum值的语句。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 protected <T> List<T> selectColumn(String sqlId, Map<String, Object> paramaters) { 2 QueryRunner qr = new QueryRunner(); 3 try { 4 Connection conn = TransactionManager.getConnection(); 5 6 // 保存全部注入参数 7 List<Object> l = new ArrayList<Object>(); 8 9 // 获取SQL模板 10 String sql = this.sqlParser.parseSql(sqlId, paramaters, true); 11 12 // 匹配SQL中待赋值的参数 13 Matcher m = argPattern.matcher(sql); 14 while (m.find()) { 15 String arg = m.group(1); 16 Object val = paramaters.get(arg); 17 if (val instanceof List) { 18 StringBuilder sb = new StringBuilder(); 19 List v = (List) val; 20 int iMax = v.size() - 1; 21 for (int i = 0;; i++) { 22 sb.append("?"); 23 l.add(v.get(i)); 24 if (i == iMax) { 25 break; 26 } 27 sb.append(", "); 28 } 29 sql = sql.replace("#" + arg + "#", sb.toString()); 30 } else { 31 l.add(val); 32 } 33 } 34 sql = sql.replaceAll("#([^\\s]+)#", "?"); 35 // 执行SQL获取返回指定列的集合 36 return qr 37 .query(conn, sql, new ColumnListHandler<T>(1), l.toArray()); 38 } catch (SQLException e1) { 39 throw new RuntimeException(e1.getMessage(), e1); 40 } 41 }
selectColumns(String sqlId, Map<String, Object> paramaters)方法
与selectColumn方法类似,不做介绍了
5、获取ResultSetHandler对象
以下两个方法用于获取解析结果集的ResultSetHandler对象,如果子类需要使用不同的方式解析ResultSet就要重写这两个方法
1 protected ResultSetHandler<E> getResultSetHandler() { 2 return new BeanHandler<E>(this.entityClass); 3 } 4 5 protected ResultSetHandler<List<E>> getResultSetListHandler() { 6 return new BeanListHandler<E>(this.entityClass); 7 }