package com.yanzuoguang.dao.Impl; import com.yanzuoguang.dao.TableAnnotation; import com.yanzuoguang.db.DbExecute; import com.yanzuoguang.db.Impl.DbRow; import com.yanzuoguang.util.base.ObjectHelper; import com.yanzuoguang.util.cache.MemoryCache; import com.yanzuoguang.util.exception.CodeException; import com.yanzuoguang.util.helper.StringHelper; import com.yanzuoguang.util.vo.MapRow; import com.yanzuoguang.util.vo.PageSizeData; import com.yanzuoguang.util.vo.PageSizeReq; import com.yanzuoguang.util.vo.PageSizeReqVo; import javax.annotation.Resource; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * SQL语句基本操作类 */ public abstract class BaseDaoSql { /** * 定义列表记录缓存对象 */ protected MemoryCache cacheList = new MemoryCache(0); /** * 数据库执行类 */ @Resource protected DbExecute db; /** * 当前Dao的表结构SQL语句信息 */ protected TableSqlCache Table = null; /** * 缓存的表结构和SQL语句 */ protected static MemoryCache<TableSqlCache> Cache = new MemoryCache<>(); /** * 获取数据库执行类 * * @return */ protected DbExecute getDb() { return db; } /** * 构造函数 */ public BaseDaoSql() { this.initTable(); } /** * 初始化当前类对应的SQL语句对象 */ private void initTable() { String cls = this.getClass().getName(); // 外检测,防止锁影响性能 this.Table = Cache.get(cls); // 判断是否已经取到对象 if (this.Table == null) { this.Table = new TableSqlCache(); Cache.put(cls, this.Table); this.init(); } } /** * 注册SQL语句 */ protected abstract void init(); /** * 注册表结构,会根据实体以及表名自动创建Create、update、remove、Load等SQL语句 * * @param clsModel 操作的实体,主键放在第一位,其他字段放到后面;需要注意的是必需和表结构对应起来,会有隐性BUG,比如说在实体中增加了字段,会导致增加修改失败 * @return 当前的表结构 */ protected TableSqlCache register(Class<?> clsModel) { TableAnnotation annotation = clsModel.getAnnotation(TableAnnotation.class); if (annotation == null) { throw new CodeException("该页面未绑定表"); } return this.register(annotation.value(), clsModel); } /** * 注册表结构,会根据实体以及表名自动创建Create、update、remove、Load等SQL语句 * * @param tableName 表名,数据库中的表名 * @param clsModel 操作的实体,主键放在第一位,其他字段放到后面;需要注意的是必需和表结构对应起来,会有隐性BUG,比如说在实体中增加了字段,会导致增加修改失败 * @return 当前的表结构 */ protected TableSqlCache register(String tableName, Class<?> clsModel) { // 生成表结构 TableStruct table = new TableStruct(tableName, clsModel); // 根据表结构生成基本的SQL语句 table.init(this.Table); return this.Table; } /** * 获取SQL语句 * * @param name 需要获取的SQL语句的名称 * @return 对应名称的SQL语句 */ protected SqlData getSql(String name) { return getSql(name, true); } /** * 获取SQL语句 * * @param name 需要获取的SQL语句的名称 * @return 对应名称的SQL语句 */ protected SqlData getSql(String name, boolean isThrow) { if (this.Table == null) { throw new CodeException("类" + this.getClass().getName() + "未发现表结构"); } SqlData sql = this.Table.Sqls.get(name); if (isThrow && sql == null) { throw new CodeException("类" + this.getClass().getName() + "未发现SQL语句" + name); } return sql; } /** * 当更新数据时 * * @param model 更新时的数据 */ protected void onUpdateSql(Object model) { // 删除所有查询的缓存 cacheList.clear(); } /** * 执行更新语句 * * @param sqlData 需要执行的语句 * @param model 执行的实体 * @return 影响的行数 */ protected int updateSql(SqlData sqlData, Object model) { List<Object> paras = new ArrayList<Object>(); String sql = this.getQueryPara(paras, sqlData, model); int ret = db.update(this.getClass(), sqlData.Name, sql, paras.toArray()); this.onUpdateSql(model); return ret; } /** * 更新SQL语句 * * @param sqlName 参数对应的名称 * @param model 参数对应的实体 * @return 修改成功,返回修改条数,否则为0 */ public int updateSql(String sqlName, Object model) { SqlData sqlData = this.getSql(sqlName); int ret = updateSql(sqlData, model); return ret; } /** * 根据SQL语句信息查询第一个单元格 * * @param sqlData SQL语句信息 * @param model 前台参数 * @return 查询的结果 */ protected Object queryCell(SqlData sqlData, Object model) { List<Object> paras = new ArrayList<Object>(); String sql = this.getQueryPara(paras, sqlData, model); Object cell = this.queryCellWithCache(sql, sqlData.Name, paras.toArray()); return cell; } /** * 根据SQL名称获取第一个单元格 * * @param sqlName 需要执行的SQL语句的名称 * @param model 需要转化的实体 * @return 处理参数 */ protected Object queryCell(String sqlName, Object model) { return this.queryCell(this.getSql(sqlName), model); } /** * 根据SQL语句信息查询数据 * * @param cls 数据结果类型 * @param sqlData SQL语句信息 * @param model 前台参数 * @param <T> 返回数据类型 * @return 查询的结果 */ protected <T extends Object> List<T> queryData(Class<T> cls, SqlData sqlData, Object model) { List<Object> paras = new ArrayList<Object>(); String sql = this.getQueryPara(paras, sqlData, model); List<T> list = this.queryWithCache(cls, sqlData.Name, sql, paras.toArray()); return list; } /** * 查询不分页数据 * * @param cls 数据结果类型 * @param sqlData SQL数据 * @param model 前台参数 * @param <T> 返回数据类型 */ protected <T extends Object> void queryData(Class<T> cls, DbRow<T> handle, SqlData sqlData, Object model) { List<Object> paras = new ArrayList<Object>(); String sql = this.getQueryPara(paras, sqlData, model); // 查询数据 db.query(this.getClass(), cls, handle, sqlData.Name, sql, paras); } /** * 查询不分页数据 * * @param cls 数据结果类型 * @param sqlName SQL语句名称 * @param model 前台参数 * @param <T> 返回数据类型 * @return 查询的结果 */ protected <T extends Object> List<T> query(Class<T> cls, String sqlName, Object model) { SqlData sqlData = this.getSql(sqlName); return queryData(cls, sqlData, model); } /** * 查询不分页数据 * * @param cls 数据结果类型 * @param handle 需要处理的数据 * @param sqlName SQL语句名称 * @param model 前台参数 * @return 查询的结果 */ protected <T extends Object> void query(Class<T> cls, DbRow<T> handle, String sqlName, Object model) { SqlData sqlData = this.getSql(sqlName); queryData(cls, handle, sqlData, model); } /** * 查询第一条数据 * * @param cls 数据结果类型 * @param sqlName SQL语句名称 * @param model 前台参数 * @param <T> 返回数据类型 * @return 查询的结果 */ protected <T extends Object> T queryFirst(Class<T> cls, String sqlName, Object model) { PageSizeReq pageSize = new PageSizeReqVo(); pageSize.setPageIndex(1); pageSize.setPageSize(1); List<T> list = this.queryPageData(cls, pageSize, sqlName, model); T retVal = list.size() > 0 ? list.get(0) : null; return retVal; } /** * 查询分页数据,仅仅只是查询分页中的数据,不查询分页信息。如:包含的总数据数量 * * @param cls 数据结果类型 * @param pageSize 分页参数 * @param sqlName SQL语句名称 * @param model 前台参数 * @param <T> 返回数据类型 * @return 查询的结果 */ protected <T extends Object> List<T> queryPageData(Class<T> cls, PageSizeReq pageSize, String sqlName, Object model) { SqlData from = this.getSql(sqlName); // 对SQL语句进行分页处理 SqlData to = from.copy(); to.Sql = from.Sql; to.addCode("{LIMIT}", " LIMIT " + pageSize.getPageStart() + "," + pageSize.getPageSize()); return queryData(cls, to, model); } /** * 查询分页数据,仅仅只是查询分页中的数据。如:包含的总数据数量 * * @param cls 数据结果类型 * @param pageSize 分页参数 * @param sqlName SQL语句名称 * @param model 前台参数 * @param <T> 返回数据类型 * @return 查询的结果 */ protected <T extends Object> PageSizeData<T> queryPage(Class<T> cls, PageSizeReqVo pageSize, String sqlName, Object model) { // 获取需要执行的SQL语句 SqlData from = this.getSql(sqlName); // 设置基本参数值 PageSizeData<T> data = new PageSizeData<T>(); data.setPageIndex(pageSize.getPageIndex()); data.setPageSize(pageSize.getPageSize()); // 对SQL语句进行分页处理 SqlData to = from.copy(); to.addCode("{LIMIT}", " LIMIT " + pageSize.getPageStart() + "," + pageSize.getPageSize()); // 按照分页查询数据 List<Object> paras = new ArrayList<Object>(); String sql = this.getQueryPara(paras, to, model); // 查询实体数据 List<T> list = this.queryWithCache(cls, sqlName, sql, paras.toArray()); data.setList(list); // 查询分页总条数的SQL语句 String sqlTo = sql; // 获取分页查询的SQL语句 SqlData fromPageSize = getSql(sqlName + TableSqlCache.PAGE_SIZE_TAG, false); if (fromPageSize != null) { sqlTo = getQueryPara(new ArrayList<>(), fromPageSize, model); } // 查询总数据量 String sqlSize = "SELECT COUNT(1) FROM (" + sqlTo.replaceAll("(?i)(ORDER\\s+BY\\s+[^)]+){0,1}(limit\\s+\\d+,\\d+\\s*){0,1}$", "") + ") t"; data.setPageTotal(StringHelper.toInt(queryCellWithCache(String.format("%s.Size", sqlName), sqlSize, paras.toArray()))); return data; } /** * 根据SQL语句信息查询数据 * * @param sqlData SQL语句信息 * @param model 前台参数 * @return 查询的结果 */ protected List<MapRow> queryData(SqlData sqlData, Object model) { return this.queryData(MapRow.class, sqlData, model); } /** * 查询不分页数据 * * @param sqlName SQL语句名称 * @param model 前台参数 * @return 查询的结果 */ protected List<MapRow> query(String sqlName, Object model) { return this.query(MapRow.class, sqlName, model); } /** * 查询第一条数据 * * @param sqlName SQL语句名称 * @param model 前台参数 * @return 查询的结果 */ protected MapRow queryFirst(String sqlName, Object model) { return this.queryFirst(MapRow.class, sqlName, model); } /** * 查询分页数据,仅仅只是查询分页中的数据,不查询分页信息。如:包含的总数据数量 * * @param pageSize 分页参数 * @param sqlName SQL语句名称 * @param model 前台参数 * @return 查询的结果 */ protected List<MapRow> queryPageData(PageSizeReqVo pageSize, String sqlName, Object model) { return this.queryPageData(MapRow.class, pageSize, sqlName, model); } /** * 查询分页数据,仅仅只是查询分页中的数据,不查询分页信息。如:包含的总数据数量 * * @param pageSize 分页参数 * @param sqlName SQL语句名称 * @param model 前台参数 * @return 查询的结果 */ protected PageSizeData<MapRow> queryPage(PageSizeReqVo pageSize, String sqlName, Object model) { return this.queryPage(MapRow.class, pageSize, sqlName, model); } /** * 获取SQL语句的参数 * * @param paras SQL语句 * @param sqlData SQL语句的参数 * @param model 参数的实体 * @return SQL条件 */ protected String getQueryPara(List<Object> paras, SqlData sqlData, Object model) { // 定义可替换片段 // String fromNext = "{FIELD_Front}"; // String fromPrev = "{FIELD}"; // String wherePrev = "{INNER}"; String[] lastCode = new String[]{"{WHERE}", "{GROUP}", "{HAVING}", "{ORDER}", "{LIMIT}"}; // 将SQL语句进行代码片段追加 String sql = sqlData.Sql; for (String code : lastCode) { if (sql.indexOf(code) == -1) { sql += code; } } // 处理字段以及代码片段 Map<String, List<String>> codeMap = new HashMap<String, List<String>>(); // 代码片段缓存 // 循环处理字段 for (SqlDataField field : sqlData.sqlDataFields) { // 获取值 Object val = ObjectHelper.get(model, field.paraName); // 不需要输入参数,仅仅只是代码片段 boolean isCode = StringHelper.isEmpty(field.paraName); // 判断是否属于 if (!isCode) { // 判断是否属于条件 boolean isCond = field.codes.size() > 0 && field.auto; // 处理参数 if (!isCond) { // 进行SQL语句参数替换,后面增加一个空格,方便后续用正则表达式进行替换处理 sql = sql.replaceFirst("\\?", "@" + field.paraName + " "); } if (isCond && StringHelper.isEmpty(val)) { continue; } } // 判断代码片段是否合法 if (field.codes.size() % 2 == 1) { throw new CodeException("代码片段" + this.getClass().getSimpleName() + ":" + sqlData.Name + ":" + field.paraName + "为单数"); } // 处理代码片段 for (int i = 0; i < field.codes.size(); i = i + 2) { String codeName = field.codes.get(i); String codeValue = field.codes.get(i + 1); codeValue = codeValue.replaceAll("\\?", "@" + field.paraName + " "); addCodeMap(codeMap, codeName, codeValue); } } // 正则表达式匹配代码片段,并根据代码片段获取数组进行增加 { String regex = "\\{.*?\\}"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(sql); // 寻找到的代码片段 不包含分括号 while (m.find()) { String name = m.group(); // m.group(1); List<String> codes = codeMap.containsKey(name) ? codeMap.get(name) : new ArrayList<String>(); String code = String.join("", codes); sql = sql.replace(name, code); } } // 通过正则表达式处理参数 @Name 并替换SQL语句成 ? { String regex = "@([a-zA-Z0-9_]+)"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(sql); // 寻找到的代码片段 不包含分括号 while (m.find()) { // SQL参数名称 @Name\s String name = m.group(); // 对应的前台输入字段 Name String field = m.group(1); // 根据输入字段从参数中取值 Object val = ObjectHelper.get(model, field); // 判断是否为数组 if (val != null && (val instanceof List || val.getClass().isArray())) { sql = getListSql(paras, sql, name, val); } else { // 当参数不为数组时,则直接增加 sql = sql.replaceFirst(name, "?"); val = this.getParaValue(val); paras.add(val); } } } return sql; } /** * 获取处理后的参数值 * * @param val 获取参数值 * @return 处理后的参数值 */ private Object getParaValue(Object val) { if (val instanceof Boolean) { val = 0; } val = StringHelper.toString(val); return val; } /** * 获取列表的SQL语句 * * @param paras 参数 * @param sql SQL语句 * @param name 名称 * @param val 值 * @return 获取包含列表的SQL语句的值 */ private String getListSql(List<Object> paras, String sql, String name, Object val) { List list; // 判断处理 boolean isList = val instanceof List; if (isList) { list = (List) val; } else { Object[] arr = (Object[]) val; list = Arrays.asList(arr); } int length = list.size(); // 进行循环 StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { Object item = list.get(i); // 判断列表是否为常规对象 if (item == null || item instanceof Number || item.getClass().isEnum() || item instanceof String || item instanceof Date) { addArrayIn(paras, sb, i, item); } else if (item instanceof CaseSqlModel) { addArrayCase(paras, sb, i, (CaseSqlModel) item); } else { addArrayTable(paras, sb, i, item); } } // 生成最终的SQL语句 sql = sql.replaceFirst(name, sb.toString()); return sql; } /** * 添加数组型参数 * * @param paras 参数列表 * @param sb 字符串数组 * @param i 序号 * @param from 参数值 */ private void addArrayIn(List<Object> paras, StringBuilder sb, int i, Object from) { // 添加连接字符串 if (i > 0) { sb.append(","); } // 添加参数SQL语句 sb.append("?"); Object to = this.getParaValue(from); paras.add(to); } /** * 添加 CASE型字段 * * @param paras 参数列表 * @param sb 字符串数组 * @param i 序号 * @param from 参数值 */ private void addArrayCase(List<Object> paras, StringBuilder sb, int i, CaseSqlModel from) { // 添加连接字符串 if (i > 0) { sb.append(","); } switch (from.getGroupType()) { case CaseSqlModel.GroupTypeSUM: sb.append("SUM"); break; case CaseSqlModel.GroupTypeCOUNT: sb.append("COUNT"); break; case CaseSqlModel.GroupTypeMAX: sb.append("MAX"); break; case CaseSqlModel.GroupTypeMIN: sb.append("MIN"); break; case CaseSqlModel.GroupTypeAVG: sb.append("AVG"); break; default: throw new CodeException("统计类型[" + from.getGroupType() + "]不支持"); } sb.append("( CASE WHEN "); sb.append(from.getCaseField()); sb.append(" "); if (from.getCaseValue() != null) { sb.append("=?"); paras.add(from.getCaseValue()); } sb.append(from.getAndWhere()); sb.append(" THEN "); if (!StringHelper.isEmpty(from.getValueField())) { sb.append(from.getValueField()); } else if (!StringHelper.isEmpty(from.getValue())) { sb.append("?"); paras.add(from.getValue()); } sb.append(" ELSE 0 END ) AS "); sb.append(from.getToName()); } /** * 添加表格型参数,将列表转换成表格 * * @param paras 参数列表 * @param sb 字符串数组 * @param i 序号 * @param from 参数值 */ private void addArrayTable(List<Object> paras, StringBuilder sb, int i, Object from) { // 添加连接字符串 if (i > 0) { sb.append(" UNION ALL "); } // 将对象转换为 Map 对象 Map to = new HashMap<String, Object>(); if (from instanceof Map) { to = (Map) from; } else { ObjectHelper.writeWithFromClass(to, from); } // 生成SQL语句 int column = 0; sb.append(" SELECT "); // 处理列 for (Object oItemKey : to.keySet()) { if (column > 0) { sb.append(","); } String itemKey = StringHelper.toString(oItemKey); sb.append("? AS " + itemKey); // 处理参数 Object itemValue = to.get(oItemKey); itemValue = this.getParaValue(itemValue); paras.add(itemValue); column++; } } /** * 将代码片段添加到SQL语句中 * * @param codeMap 映射关系 * @param name 执行的代码片段 * @param code 代码片段 */ private void addCodeMap(Map<String, List<String>> codeMap, String name, String code) { if (!codeMap.containsKey(name)) { codeMap.put(name, new ArrayList<String>()); } List<String> arr = codeMap.get(name); arr.add(code); } /** * 根据缓存查询第一个单元格 * * @param sqlName 需要执行的SQL语句名称 * @param sql 需要执行的SQL语句 * @param paras SQL语句的参数 * @return 查询的结果 */ protected Object queryCellWithCache(String sqlName, String sql, Object... paras) { // 生成缓存的主键 String cacheKey = getCacheKey(Object.class, sql, paras); // 获取缓存 Object cache = this.cacheList.get(cacheKey); // 返回缓存 if (cache != null) { return cache; } // 查询数据 Object ret = db.queryCell(this.getClass(), sqlName, sql, paras); // 写入缓存 this.cacheList.put(cacheKey, ret); return ret; } /** * 执行查询SQL语句,并将数据缓存起来 * * @param cls 返回的类型 * @param sqlName 需要执行的SQL语句名称 * @param sql 需要执行的SQL语句 * @param paras 执行的SQL语句的参数 * @param <T> 返回的泛型 * @return 需要返回的数据 */ protected <T> List<T> queryWithCache(Class<T> cls, String sqlName, String sql, Object... paras) { // 生成缓存的主键 String cacheKey = getCacheKey(cls, sql, paras); // 获取缓存 Object cache = this.cacheList.get(cacheKey); // 返回缓存 if (cache != null) { return (List<T>) cache; } // 查询数据 List<T> ret = db.query(this.getClass(), cls, sqlName, sql, paras); // 写入缓存 this.cacheList.put(cacheKey, ret); return ret; } /** * 获取缓存的键值对 * * @param cls 结果类型 * @param sql SQL语句 * @param paras SQL语句参数 * @return 返回的结果 */ protected String getCacheKey(Class<?> cls, String sql, Object... paras) { StringBuilder sb = new StringBuilder(); sb.append(cls.getName()); sb.append(":"); sb.append(sql); for (Object para : paras) { sb.append(":"); sb.append(para); } return StringHelper.md5(sb.toString()); } }