package com.yanzuoguang.dao.impl; import com.yanzuoguang.dao.BaseDao; import com.yanzuoguang.dao.DaoConst; import com.yanzuoguang.dao.QueryPara; import com.yanzuoguang.db.DbExecute; import com.yanzuoguang.util.YzgError; import com.yanzuoguang.util.base.ObjectHelper; import com.yanzuoguang.util.exception.CodeException; import com.yanzuoguang.util.helper.DateHelper; import com.yanzuoguang.util.helper.StringHelper; import com.yanzuoguang.util.vo.*; import java.util.*; /** * 数据库操作的基本工具类 * 1. 实现了基本的增删该查 * 2. 实现了统计的增肌和修改 * 3. 实现了一定功能的基本验证,验证数据是否存在 * 4. 数据主键的获取等功能。 * 5. 获取自增等功能 * * @author 颜佐光 */ public abstract class BaseDaoImpl extends BaseDaoSql implements BaseDao { /** * 构造函数 */ public BaseDaoImpl() { } public BaseDaoImpl(DbExecute db, TableSqlCache table) { super(db, table); } /** * 获取当前主键 * * @return */ private String getIdentity() { return StringHelper.toString(this.getDb().queryCell(this.getClass(), "GET_KEY", "SELECT @@IDENTITY")); } protected String createReplace(String sqlName, Object model) { this.checkTable(); // 判断主键是字符串和需要生成主键 boolean isKeyString = this.table.getTable().getKeyType() == String.class; String keyString = this.table.initKeyValue(model); // 检测数据合法性 this.check(DaoConst.OPERATOR_TYPE_CREATE, keyString, model); // 执行创建的SQL语句 int ret = updateSql(sqlName, model); // 判断是否需要获取自增编号(主键为整形) if (StringHelper.isEmpty(keyString) && !isKeyString) { keyString = this.getIdentity(); this.table.setKeyValue(model, keyString); } // 最终处理 this.created(model); // 返回执行结果 return ret > 0 ? keyString : ""; } protected List<String> createReplaceList(String sqlName, Collection collection) { if (collection == null || collection.isEmpty()) { return new ArrayList<>(); } this.checkTable(); int size = collection.size(); // 写入主键,用于排序修改,防止互相锁数据 for (Object item : collection) { this.table.initKeyValue(item); } // 获取排序后的数据 List list = this.table.getKeySort(collection); boolean isKeyString = this.table.getTable().getKeyType() == String.class; boolean isEmpty = true; // 循环创建 for (Object item : list) { // 判断主键是字符串和需要生成主键 String keyString = this.table.initKeyValue(item); // 检测数据合法性 this.check(DaoConst.OPERATOR_TYPE_CREATE, keyString, item); // 判断是否为空 if (!StringHelper.isEmpty(keyString)) { isEmpty = false; } } // 获取创建语句 SqlData sqlData = this.getSql(sqlName); // 最终sql StringBuilder sqlList = new StringBuilder(); // 最终列表 List<Object> paraList = new ArrayList<>(); for (Object item : list) { List<Object> paras = new ArrayList<>(); String sql = this.getPara(paras, sqlData, item, true); if (sqlList.length() > 0) { sql = sql.replaceAll(DaoConst.SQL_LIST_FROM, DaoConst.SQL_LIST_TO); } sqlList.append(sql); paraList.addAll(paras); } // 最终执行的更新语句 int ret = this.getDb().update(this.getClass(), sqlData.getName(), sqlList.toString(), paraList.toArray()); // 判断是否需要获取自增编号(主键为整形) if (isEmpty && !isKeyString) { long identity = StringHelper.toInt(this.getIdentity()); int pos = 0; // 回写主键 for (Object item : list) { pos++; String keyString = StringHelper.toString(identity - size + pos); this.table.setKeyValue(item, keyString); } } // 触发更新事件 for (Object item : list) { this.onUpdateSql(item); } // 最后获取主键集合 return this.table.getCollectionRet(collection); } /** * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。 * * @param model 需要创建的数据 * @return 创建的主键编号, 创建成功,返回主键,否则为空 */ @Override public String create(Object model) { return createReplace(DaoConst.CREATE, model); } /** * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。 * * @param model 需要创建的数据 * @return 创建的主键编号, 创建成功,返回主键,否则为空 */ @Override public String replace(Object model) { return createReplace(DaoConst.REPLACE, model); } /** * 修改数据 * * @param model 需要修改的数据 * @return 删除的主键编号 */ @Override public String update(Object model) { this.checkTable(); String keyString = this.table.getKeyValue(model); if (StringHelper.isEmpty(keyString)) { throw YzgError.getRuntimeException("029", this.table.getTable().getName()); } this.check(DaoConst.OPERATOR_TYPE_UPDATE, keyString, model); SqlData sqlData = this.getSql(DaoConst.UPDATE); int ret = updateSql(sqlData, model); if (ret == 0) { throw YzgError.getRuntimeException("030"); } return ret > 0 ? keyString : ""; } /** * 保存数据 * * @param model 需要保存的数据 * @return 保存的主键编号 */ @Override public String save(Object model) { this.checkTable(); String id = this.table.getKeyValue(model); if (StringHelper.isEmpty(id)) { return create(model); } else { return update(model); } } /** * 删除数据 * * @param model 需要删除的数据 * @return 删除的主键编号 */ @Override public int remove(Object model) { this.checkTable(); // 获取来源主键 Object from = model; String keyString = this.table.getInputKey(from); // 调用删除日志 this.check(DaoConst.OPERATOR_TYPE_REMOVE, keyString, from); // 当主键存在值时,直接通过主键删除 from = this.table.getKeyObject(from, keyString); TableStruct tableStruct = this.table.getTable(); TableFieldVo remove = tableStruct.getRemove(); addModelFromValue(from, model, remove, StringHelper.EMPTY); addModelFromValue(from, model, remove, DaoConst.OLD_FLAG); // 处理来源值 for (TableFieldVo fieldVo : tableStruct.getRemoveUpdate()) { addModelFromValue(from, model, fieldVo, StringHelper.EMPTY); } // 调用删除语句 SqlData sqlData = this.getSql(DaoConst.REMOVE); return updateSql(sqlData, from); } private void addModelFromValue(Object from, Object model, TableFieldVo fieldVo, String tag) { if (fieldVo == null) { return; } String fieldName = fieldVo.inputName + tag; Object fromValue = ObjectHelper.get(model, fieldName); ObjectHelper.set(from, fieldName, fromValue); } /** * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。 * * @param collection 需要创建的数据 * @return 创建的主键编号 */ @Override public List<String> createList(Collection collection) { return this.createReplaceList(DaoConst.CREATE, collection); } /** * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。 * * @param model 需要创建的数据 * @return 创建的主键编号 */ @Override public List<String> createArray(Object... model) { return createList(Arrays.asList(model)); } /** * 修改数据 * * @param collection 需要修改的数据 * @return 删除的主键编号 */ @Override public List<String> updateList(Collection collection) { this.checkTable(); // 获取排序后的数据 List list = this.table.getKeySort(collection); for (Object item : list) { this.update(item); } // 最后获取主键集合 return this.table.getCollectionRet(collection); } /** * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。 * * @param model 需要创建的数据 * @return 创建的主键编号 */ @Override public List<String> replaceArray(Object... model) { return replaceList(Arrays.asList(model)); } /** * 修改数据 * * @param collection 需要修改的数据 * @return 删除的主键编号 */ @Override public List<String> replaceList(Collection collection) { return this.createReplaceList(DaoConst.REPLACE, collection); } /** * 修改数据 * * @param model 需要修改的数据 * @return 删除的主键编号 */ @Override public List<String> updateArray(Object... model) { return updateList(Arrays.asList(model)); } /** * 保存数据,有主键时修改,无主键时创建 * * @param collection 需要保存的数据 * @return 保存的主键编号 */ @Override public List<String> saveList(Collection collection) { // 创建列表 List<Object> creates = new ArrayList<>(); // 修改列表 List<Object> updates = new ArrayList<>(); // 将保存的集合数据划分为创建列表和修改列表 for (Object item : collection) { String keyValue = this.table.getKeyValue(item); if (StringHelper.isEmpty(keyValue)) { creates.add(item); } else { updates.add(item); } } // 首先执行更新,防止和创建时发生冲突 this.updateList(updates); // 然后执行创建SQL语句 this.createList(creates); // 最后获取主键集合 return this.table.getCollectionRet(collection); } /** * 保存数据,有主键时修改,无主键时创建 * * @param model 需要保存的数据 * @return 保存的主键编号 */ @Override public List<String> saveArray(Object... model) { return saveList(Arrays.asList(model)); } /** * 删除数据,可以用于父子表删除,如通过订单删除游客信息 visitorDao.remove({orderId:1}); * int field; * * @param collection 需要删除的数据,可以是主键字符串(Int),或者是包含主键的实体,或者是包含其他非主键的实体完全匹配. * @return 删除的记录数量 */ @Override public int removeList(Collection collection) { this.checkTable(); // 获取删除的列表,会按照主键顺序进行删除,没有主键时,不进行排序 List list = this.table.getKeySort(collection); // 返回影响的行数 int ret = 0; for (Object item : list) { ret += this.remove(item); } return ret; } /** * 删除数据,可以用于父子表删除,如通过订单删除游客信息 visitorDao.remove({orderId:1}); * int field; * * @param model 需要删除的数据,可以是主键字符串(Int),或者是包含主键的实体,或者是包含其他非主键的实体完全匹配. * @return 删除的记录数量 */ @Override public int removeArray(Object... model) { return removeList(Arrays.asList(model)); } /** * 检测数据 * * @param operatorType 操作方法类型 * @param keyString 主键 * @param model 需要检测的数据 */ protected void check(int operatorType, String keyString, Object model) { if (operatorType == DaoConst.OPERATOR_TYPE_CREATE || operatorType == DaoConst.OPERATOR_TYPE_UPDATE) { if (model instanceof InitDao) { InitDao to = (InitDao) model; to.init(); } List<SqlData> sqlArray = this.table.getSqlType(DaoConst.SQL_TYPE_EXISTS); if (sqlArray != null) { for (SqlData sql : sqlArray) { this.checkExist(sql.getName(), model, String.format("%s已经存在", sql.getName())); } } } else if (operatorType == DaoConst.OPERATOR_TYPE_REMOVE) { if (model instanceof InitRemoveDao) { InitRemoveDao to = (InitRemoveDao) model; to.initRemove(); } } } /** * 获取请求对象爱 * * @param model 来源对象 * @return */ protected Object getLoadFrom(Object model, QueryPara queryPara) { this.checkTable(); if (queryPara != null && queryPara.isFullCond()) { return model; } // 获取来源主键 return this.table.getKeyObject(model); } private <T> void checkLoadResult(T toItem, List<T> tos) { this.checkTable(); // 判断来源主键是否存在,不存在则获取加载后的主键 if (toItem != null) { String key = this.table.getKeyValue(toItem); check(DaoConst.OPERATOR_TYPE_LOAD, key, toItem); } if (tos != null) { for (T item : tos) { String key = this.table.getKeyValue(item); check(DaoConst.OPERATOR_TYPE_LOAD, key, item); } } } /** * 加载数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param queryPara 查询参数 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> T load(Object model, Class<T> resultClass, QueryPara queryPara) { // 获取来源主键 Object from = this.getLoadFrom(model, queryPara); // 通过传入数据进行加载 T to = this.queryFirst(resultClass, DaoConst.LOAD, from, queryPara); if (to == null) { return null; } // 判断来源主键是否存在,不存在则获取加载后的主键 checkLoadResult(to, null); return to; } /** * 加载数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> T load(Object model, Class<T> resultClass) { return this.load(model, resultClass, null); } /** * 加载数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param queryPara 查询参数 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> List<T> loadList(Object model, Class<T> resultClass, QueryPara queryPara) { // 获取来源主键 Object from = this.getLoadFrom(model, queryPara); // 通过传入数据进行加载 List<T> to = this.query(resultClass, DaoConst.LOAD, from, queryPara); if (to == null || to.size() == 0) { return new ArrayList<>(); } checkLoadResult(null, to); return to; } /** * 加载数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> List<T> loadList(Object model, Class<T> resultClass) { return this.loadList(model, resultClass, null); } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> PageSizeData<T> loadPage(PageSizeReqVo model, Class<T> resultClass) { return this.loadPage(model, model, resultClass, null); } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param queryPara 查询参数 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> PageSizeData<T> loadPage(PageSizeReqVo model, Class<T> resultClass, QueryPara queryPara) { return this.loadPage(model, model, resultClass, queryPara); } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> PageSizeData<T> loadPage(PageSizeReqVo pageReq, Object model, Class<T> resultClass) { return this.loadPage(pageReq, model, resultClass, null); } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param queryPara 查询参数 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> PageSizeData<T> loadPage(PageSizeReqVo pageReq, Object model, Class<T> resultClass, QueryPara queryPara) { // 获取来源主键 Object from = this.getLoadFrom(model, queryPara); // 通过传入数据进行加载 PageSizeData<T> to = this.queryPage(resultClass, pageReq, DaoConst.LOAD, from, queryPara); if (to == null || to.getPageTotal() == 0) { return to; } // 判断来源主键是否存在,不存在则获取加载后的主键 checkLoadResult(null, to.getList()); return to; } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> List<T> loadPageData(PageSizeReqVo model, Class<T> resultClass) { return this.loadPageData(model, model, resultClass, null); } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param queryPara 查询参数 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> List<T> loadPageData(PageSizeReqVo model, Class<T> resultClass, QueryPara queryPara) { return this.loadPageData(model, model, resultClass, queryPara); } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> List<T> loadPageData(PageSizeReqVo pageReq, Object model, Class<T> resultClass) { return this.loadPageData(pageReq, model, resultClass, null); } /** * 加载分页数据 * * @param model 加载数据的请求参数 * @param resultClass 需要加载的数据的类型 * @param queryPara 查询参数 * @param <T> 返回数据的类型 * @return 需要返回的数据 */ @Override public <T> List<T> loadPageData(PageSizeReqVo pageReq, Object model, Class<T> resultClass, QueryPara queryPara) { // 获取来源主键 Object from = this.getLoadFrom(model, queryPara); // 通过传入数据进行加载 List<T> to = this.queryPageData(resultClass, pageReq, DaoConst.LOAD, from, queryPara); if (to == null) { return new ArrayList<>(); } // 判断来源主键是否存在,不存在则获取加载后的主键 checkLoadResult(null, to); return to; } /** * 设置MD5主键 * * @param model * @param <T> */ @Override public <T> String setGroupId(T model) { this.checkTable(); // 判断前台实体 if (model == null) { return StringHelper.EMPTY; } String md5Field = this.table.getTable().getMD5KeyName(); // 获取前台分组的MD5标识 String md5 = StringHelper.isEmpty(md5Field) ? StringHelper.EMPTY : ObjectHelper.getString(model, md5Field);; if (StringHelper.isEmpty(md5)) { md5 = this.getMd5(DaoConst.GROUP_QUERY, model); } if (!StringHelper.isEmpty(md5Field)) { ObjectHelper.set(model, md5Field, md5); } if (this.table.getTable().getKeyType() == String.class) { this.table.setKeyValue(model, md5); } return md5; } /** * 添加统计数据 * * @param cls 类型 * @param model 实体 * @param <T> 泛型类型 * @return 增加统计的数据编号 */ @Override public <T> String addGroup(Class<T> cls, T model) { this.setGroupId(model); // 判断前台实体 if (model == null) { return ""; } // 获取标识的实体 T from; String md5Field = this.table.getTable().getMD5KeyName(); if (this.table.getTable().getKeyType() == String.class) { from = model; } else if (!StringHelper.isEmpty(md5Field)) { Map<String, Object> map = new HashMap<>(DaoConst.COLLECTION_INIT_SIZE); map.put(md5Field, ObjectHelper.get(model, md5Field)); from = this.load(map, cls); } else { from = this.queryFirst(cls, DaoConst.GROUP_QUERY, model); } // 当没有存在,则创建,否则 UpdateAdd if (from == null) { return this.create(model); } else { // 获取后台的编号,并写入到前台传入的数据上面 String keyField = this.table.getKeyField(); Object key = ObjectHelper.get(from, keyField); ObjectHelper.set(model, keyField, key); if (model instanceof InitDao) { ((InitDao) model).init(); } // 执行更新SQL语句 SqlData sqlData = this.getSql(DaoConst.GROUP_ADD); int ret = this.updateSql(sqlData, model); if (ret < 1) { return this.create(model); } else { return key.toString(); } } } /** * 添加统计数组 * * @param cls 类型 * @param models 请求实体 * @param <T> 泛型数据 * @return 返回列表 */ @Override public <T> List<String> addGroupList(Class<T> cls, List<T> models) { List<String> ret = new ArrayList<>(); for (T model : models) { ret.add(this.addGroup(cls, model)); } return ret; } /** * 添加统计数组 * * @param cls 类型 * @param models 请求实体 * @param <T> 泛型数据 * @return 返回列表 */ @Override public <T> List<String> addGroupArray(Class<T> cls, T... models) { return this.addGroupList(cls, Arrays.asList(models)); } /** * 根据SQL语句名称和实体获取MD5值 * * @param sqlName SQL语句名称 * @param model 获取加密之后的实体 * @param <T> 获取的类型 * @return 获取加密之后的值 */ protected <T> String getMd5(String sqlName, T model) { SqlData sql = this.getSql(sqlName); StringBuilder sb = new StringBuilder(); String date = StringHelper.EMPTY; for (SqlDataField field : sql.getSqlDataFields()) { List<Object> values = field.getCond().getValues(model); for (Object item : values) { if (sb.length() > 0) { sb.append(":"); } sb.append(item); if (StringHelper.isEmpty(date)) { String itemString = String.valueOf(item); if (item instanceof Date) { date = DateHelper.getDateTimeString((Date) item); } else if (DateHelper.isDateFormat(itemString)) { date = itemString; } } } } if (StringHelper.isEmpty(date)) { return StringHelper.md5(sb.toString()); } else { return StringHelper.getNewMD5Id(DateHelper.getDateTime(date), sb.toString()); } } /** * 添加统计数据列表 * * @param sqlName 需要执行的SQL语句名称 * @param model 需要执行的SQL语句的实体 * @return */ protected int addGroupList(String sqlName, Object model) { this.updateSql(sqlName + "_GroupInit", model); return this.updateSql(sqlName + "_GroupAdd", model); } /*** * 查询数据是否存在,当存在时修改,否则增加 * @param cls 需要创建的实体的类型 * @param requests 前台参数,不能包含主键以及其他不需要修改的字段 * @return 保存成功,返回保存的ID,保存失败,返回空值 */ @Override public <T> List<String> saveByLoadArray(Class<T> cls, Object... requests) { return this.saveByLoadList(cls, Arrays.asList(requests)); } /*** * 查询数据是否存在,当存在时修改,否则增加 * @param cls 需要创建的实体的类型 * @param requests 前台参数,不能包含主键以及其他不需要修改的字段 * @return 保存成功,返回保存的ID,保存失败,返回空值 */ @Override public <T> List<String> saveByLoadList(Class<T> cls, List requests) { List<String> ret = new ArrayList<>(); if (requests != null) { for (Object model : requests) { ret.add(this.saveByLoad(cls, model)); } } return ret; } /*** * 查询数据是否存在,当存在时修改,否则增加 * @param cls 需要创建的实体的类型 * @param request 前台参数,不能包含主键以及其他不需要修改的字段 * @return 保存成功,返回保存的ID,保存失败,返回空值 */ @Override public <T> String saveByLoad(Class<T> cls, Object request) { this.checkTable(); String key = this.table.getInputKey(request); T from = null; if (!StringHelper.isEmpty(key)) { from = this.load(request, cls); return saveFromCreate(cls, from, request); } return saveFromCreate(cls, from, request); } /** * 根据from来创建对象,对象存在时修改,对象不存在时创建 * * @param cls 自动创建的对象类型 * @param from 需要修改的对象,当该对象不存在时创建数据 * @param request 需要创建的对象 * @param <T> 创建的数据类型 * @return 创建成功的编号 */ protected <T> String saveFromCreate(Class<T> cls, T from, Object request) { this.checkTable(); if (from == null) { try { from = cls.newInstance(); } catch (Exception ex) { throw YzgError.getRuntimeException(ex, "046", cls.getName()); } ObjectHelper.writeWithFrom(from, request); return this.create(from); } else { TableStruct tableStruct = this.table.getTable(); // 写入历史版本号 TableFieldVo version = tableStruct.getVersion(); if (version != null) { Object hisVersion = ObjectHelper.get(from, version.inputName); ObjectHelper.writeWithFrom(from, request); ObjectHelper.set(from, version.inputName, hisVersion); } else { ObjectHelper.writeWithFrom(from, request); } return this.update(from); } } /*** * 查询数据是否存在,当存在时修改,否则增加 * @param cls 需要创建的实体的类型 * @param sqlName 执行的SQL语句 * @param request 前台参数,不能包含主键以及其他不需要修改的字段 * @return 保存成功,返回保存的ID,保存失败,返回空值 */ public <T> String saveWith(Class<T> cls, String sqlName, Object request) { this.checkTable(); if (request instanceof InitDao) { ((InitDao) request).init(); } TableStruct tableStruct = this.table.getTable(); // 获取所有条件字段 MapRow condition = new MapRow(); List<TableFieldVo> fields = tableStruct.getFields(DaoConst.FIELD_SAVE_WITH); for (TableFieldVo field : fields) { condition.put(field.inputName, ObjectHelper.get(request, field.inputName)); } // 查询历史数据 T from = this.queryFirst(cls, sqlName, condition); return saveFromCreate(cls, from, request); } /** * 创建成功后的处理 * * @param model 创建的实体数据 */ protected void created(Object model) { } /** * 执行是否存在的SQL语句 * * @param sqlName SQL语句名称 * @param model 实体 * @param msg 消息 */ protected void checkExist(String sqlName, Object model, String msg) { MapRow row = this.queryFirst(sqlName, model); if (row != null) { throw new CodeException(msg); } } /** * 对数据据进行集合操作 * * @param daoVo * @return */ @Override public List<String> dataDao(DataDaoVo daoVo) { List<String> res = new ArrayList<>(); this.removeList(daoVo.getRemoves()); res.addAll(this.updateList(daoVo.getUpdates())); res.addAll(this.createList(daoVo.getCreates())); return res; } }