Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.sagacity.sqltoy.support.SqlToyDaoSupport Maven / Gradle / Ivy
package org.sagacity.sqltoy.support;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.sql.Connection;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.sagacity.sqltoy.SqlToyConstants;
import org.sagacity.sqltoy.SqlToyContext;
import org.sagacity.sqltoy.SqlToyThreadDataHolder;
import org.sagacity.sqltoy.callback.DataSourceCallbackHandler;
import org.sagacity.sqltoy.callback.StreamResultHandler;
import org.sagacity.sqltoy.callback.UpdateRowHandler;
import org.sagacity.sqltoy.config.SqlConfigParseUtils;
import org.sagacity.sqltoy.config.model.DataType;
import org.sagacity.sqltoy.config.model.DataVersionConfig;
import org.sagacity.sqltoy.config.model.EntityMeta;
import org.sagacity.sqltoy.config.model.FieldMeta;
import org.sagacity.sqltoy.config.model.ShardingStrategyConfig;
import org.sagacity.sqltoy.config.model.SqlToyConfig;
import org.sagacity.sqltoy.config.model.SqlType;
import org.sagacity.sqltoy.config.model.Translate;
import org.sagacity.sqltoy.dialect.DialectFactory;
import org.sagacity.sqltoy.dialect.executor.ParallQueryExecutor;
import org.sagacity.sqltoy.dialect.utils.DialectUtils;
import org.sagacity.sqltoy.exception.DataAccessException;
import org.sagacity.sqltoy.integration.DistributeIdGenerator;
import org.sagacity.sqltoy.link.Batch;
import org.sagacity.sqltoy.link.Delete;
import org.sagacity.sqltoy.link.Elastic;
import org.sagacity.sqltoy.link.Execute;
import org.sagacity.sqltoy.link.Load;
import org.sagacity.sqltoy.link.Mongo;
import org.sagacity.sqltoy.link.Query;
import org.sagacity.sqltoy.link.Save;
import org.sagacity.sqltoy.link.Store;
import org.sagacity.sqltoy.link.TableApi;
import org.sagacity.sqltoy.link.TreeTable;
import org.sagacity.sqltoy.link.Unique;
import org.sagacity.sqltoy.link.Update;
import org.sagacity.sqltoy.model.CacheMatchFilter;
import org.sagacity.sqltoy.model.ColumnMeta;
import org.sagacity.sqltoy.model.EntityQuery;
import org.sagacity.sqltoy.model.EntityUpdate;
import org.sagacity.sqltoy.model.IgnoreCaseLinkedMap;
import org.sagacity.sqltoy.model.IgnoreCaseSet;
import org.sagacity.sqltoy.model.IgnoreKeyCaseMap;
import org.sagacity.sqltoy.model.LockMode;
import org.sagacity.sqltoy.model.MapKit;
import org.sagacity.sqltoy.model.Page;
import org.sagacity.sqltoy.model.ParallQuery;
import org.sagacity.sqltoy.model.ParallQueryResult;
import org.sagacity.sqltoy.model.ParallelConfig;
import org.sagacity.sqltoy.model.QueryExecutor;
import org.sagacity.sqltoy.model.QueryResult;
import org.sagacity.sqltoy.model.SaveMode;
import org.sagacity.sqltoy.model.StoreResult;
import org.sagacity.sqltoy.model.TableMeta;
import org.sagacity.sqltoy.model.TreeTableModel;
import org.sagacity.sqltoy.model.UniqueExecutor;
import org.sagacity.sqltoy.model.inner.CacheMatchExtend;
import org.sagacity.sqltoy.model.inner.EntityQueryExtend;
import org.sagacity.sqltoy.model.inner.EntityUpdateExtend;
import org.sagacity.sqltoy.model.inner.QueryExecutorExtend;
import org.sagacity.sqltoy.model.inner.TranslateExtend;
import org.sagacity.sqltoy.plugins.CrossDbAdapter;
import org.sagacity.sqltoy.plugins.IUnifyFieldsHandler;
import org.sagacity.sqltoy.plugins.datasource.DataSourceSelector;
import org.sagacity.sqltoy.plugins.id.IdGenerator;
import org.sagacity.sqltoy.translate.TranslateHandler;
import org.sagacity.sqltoy.translate.model.TranslateConfigModel;
import org.sagacity.sqltoy.utils.BeanUtil;
import org.sagacity.sqltoy.utils.BeanWrapper;
import org.sagacity.sqltoy.utils.DataSourceUtils;
import org.sagacity.sqltoy.utils.DateUtil;
import org.sagacity.sqltoy.utils.IdUtil;
import org.sagacity.sqltoy.utils.MapperUtils;
import org.sagacity.sqltoy.utils.QueryExecutorBuilder;
import org.sagacity.sqltoy.utils.ReservedWordsUtil;
import org.sagacity.sqltoy.utils.SqlUtil;
import org.sagacity.sqltoy.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @project sagacity-sqltoy
* @description sqltoy的对外服务层,基础Dao支持工具类,用于被继承扩展自己的Dao,一般情况下推荐直接使用LightDao(旧项目SqlToyLazyDao)
* @author zhongxuchen
* @version v4.0,Date:2012-6-1
* @modify Date:2012-8-8 {增强对象级联查询、删除、保存操作机制,不支持2层以上级联}
* @modify Date:2012-8-23 {新增loadAll(List entities) 方法,可以批量通过主键取回详细信息}
* @modify Date:2014-12-17 {1、增加sharding功能,改进saveOrUpdate功能,2、采用merge
* into策略;3、优化查询 条件和查询结果,变为一个对象,返回结果支持json输出}
* @modify Date:2016-3-07 {优化存储过程调用,提供常用的执行方式,剔除过往复杂的实现逻辑和不必要的兼容性,让调用过程更加可读 }
* @modify Date:2016-11-25
* {增加了分页优化功能,缓存相同查询条件的总记录数,在一定周期情况下无需再查询总记录数,从而提升分页查询的整体效率 }
* @modify Date:2017-7-13 {增加saveAllNotExist功能,批量保存数据时忽视已经存在的,避免重复性数据主键冲突}
* @modify Date:2017-11-1 {增加对象操作分库分表功能实现,精简和优化代码}
* @modify Date:2019-3-1 {增加通过缓存获取Key然后作为查询条件cache-arg 功能,从而避免二次查询或like检索}
* @modify Date:2019-6-25 {将异常统一转化成RuntimeException,不在方法上显式的抛异常}
* @modify Date:2020-4-5 {分页Page模型中设置skipQueryCount=true跳过查总记录,默认false}
* @modify Date:2020-8-25 {增加并行查询功能,为极端场景下提升查询效率,为开发者拆解复杂sql做多次查询影响性能提供了解决之道}
* @modify Date:2020-10-20 {findByQuery 增加lockMode,便于查询并锁定记录}
* @modify Date:2021-06-25
* {剔除linkDaoSupport、BaseDaoSupport,将link功能放入SqlToyDaoSupport}
* @modify Date:2021-12-23 {优化updateByQuery支持set field=field+1依据字段值进行计算的模式}
* @modify Date:2023-08-06 {增加executeMoreResultStore存储过程支持多结果返回}
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SqlToyDaoSupport {
/**
* 定义日志
*/
protected final Logger logger = LoggerFactory.getLogger(SqlToyDaoSupport.class);
/**
* 数据源
*/
protected DataSource dataSource;
// 修改模式
protected SaveMode UPDATE = SaveMode.UPDATE;
// 忽视已经存在的记录
protected SaveMode IGNORE = SaveMode.IGNORE;
/**
* sqlToy上下文定义
*/
protected SqlToyContext sqlToyContext;
/**
* 分布式id产生器
*/
private DistributeIdGenerator distributeIdGenerator = null;
/**
* 各种数据库方言实现
*/
private DialectFactory dialectFactory = DialectFactory.getInstance();
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* @todo 获取数据源,如果参数dataSource为null则返回默认的dataSource
* @param pointDataSource
* @return
*/
protected DataSource getDataSource(DataSource pointDataSource) {
return getDataSource(pointDataSource, null);
}
/**
* @TODO 获取sql对应的dataSource
* @param pointDataSource
* @param sqltoyConfig
* @return
*/
private DataSource getDataSource(DataSource pointDataSource, SqlToyConfig sqltoyConfig) {
// xml中定义的sql配置了datasource
String sqlDataSource = (null == sqltoyConfig) ? null : sqltoyConfig.getDataSource();
// 提供一个扩展,让开发者在特殊场景下可以自行定义dataSourceSelector实现数据源的选择和获取
DataSourceSelector dataSourceSelector = sqlToyContext.getDataSourceSelector();
return dataSourceSelector.getDataSource(sqlToyContext.getAppContext(), pointDataSource, sqlDataSource,
dataSource, sqlToyContext.getDefaultDataSource());
}
/**
* @todo 对象加载操作集合
* @return
*/
protected Load load() {
return new Load(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 删除操作集合
* @return
*/
protected Delete delete() {
return new Delete(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 修改操作集合
* @return
*/
protected Update update() {
return new Update(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 保存操作集合
* @return
*/
protected Save save() {
return new Save(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 查询操作集合
* @return
*/
protected Query query() {
return new Query(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 存储过程操作集合
* @return
*/
protected Store store() {
return new Store(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 唯一性验证操作集合
* @return
*/
protected Unique unique() {
return new Unique(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 树形表结构封装操作集合
* @return
*/
protected TreeTable treeTable() {
return new TreeTable(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo sql语句直接执行修改数据库操作集合
* @return
*/
protected Execute execute() {
return new Execute(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 批量执行操作集合
* @return
*/
protected Batch batch() {
return new Batch(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 提供一个获取数据库表信息和操作表信息的TableApi集合
* @return
*/
protected TableApi tableApi() {
return new TableApi(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 提供基于ES的查询(仅针对查询部分)
* @return
*/
protected Elastic elastic() {
return new Elastic(sqlToyContext, getDataSource(dataSource));
}
/**
* @todo 提供基于mongo的查询(仅针对查询部分)
* @return
*/
protected Mongo mongo() {
return new Mongo(sqlToyContext, getDataSource(dataSource));
}
/**
* @param sqlToyContext the sqlToyContext to set
*/
public void setSqlToyContext(SqlToyContext sqlToyContext) {
this.sqlToyContext = sqlToyContext;
}
/**
* @return the sqlToyContext
*/
protected SqlToyContext getSqlToyContext() {
return sqlToyContext;
}
/**
* @todo 获取sqlId 在sqltoy中的配置模型
* @param sqlKey
* @param sqlType
* @return
*/
protected SqlToyConfig getSqlToyConfig(final String sqlKey, final SqlType sqlType) {
return sqlToyContext.getSqlToyConfig(sqlKey, (sqlType == null) ? SqlType.search : sqlType, getDialect(null),
null);
}
/**
* @todo 判断数据库中数据是否唯一,true 表示唯一(可以插入),false表示不唯一(数据库已经存在该数据),用法
* isUnique(dictDetailVO,new
* String[]{"dictTypeCode","dictName"}),将会根据给定的2个参数
* 通过VO取到相应的值,作为组合条件到dictDetailVO对应的表中查询记录是否存在
* @param entity
* @param paramsNamed 对象属性名称(不是数据库表字段名称)
* @return
*/
protected boolean isUnique(final Serializable entity, final String... paramsNamed) {
return isUnique(new UniqueExecutor(entity, paramsNamed));
}
/*
* @see isUnique(final Serializable entity, final String[] paramsNamed)
*/
protected boolean isUnique(final UniqueExecutor uniqueExecutor) {
return dialectFactory.isUnique(sqlToyContext, uniqueExecutor, getDataSource(uniqueExecutor.getDataSource()));
}
protected Long getCountBySql(final String sqlOrSqlId, final Map paramsMap) {
return getCountByQuery(new QueryExecutor(sqlOrSqlId, (paramsMap == null) ? MapKit.map() : paramsMap));
}
/**
* @todo 获取数据库查询语句的总记录数
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @return Long
*/
protected Long getCountBySql(final String sqlOrSqlId, final String[] paramsNamed, final Object[] paramsValue) {
return getCountByQuery(new QueryExecutor(sqlOrSqlId, paramsNamed, paramsValue));
}
/**
* @TODO 通过entity对象来组织count查询语句
* @param entityClass
* @param entityQuery
* @return
*/
protected Long getCountByEntityQuery(Class entityClass, EntityQuery entityQuery) {
if (null == entityClass) {
throw new IllegalArgumentException("getCountByEntityQuery entityClass值不能为空!");
}
return (Long) findEntityBase(entityClass, null, (entityQuery == null) ? EntityQuery.create() : entityQuery,
entityClass, true);
}
/**
* @todo 指定数据源查询记录数量
* @param queryExecutor
* @return
*/
protected Long getCountByQuery(final QueryExecutor queryExecutor) {
QueryExecutorExtend extend = queryExecutor.getInnerModel();
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(extend.dataSource));
Long result = dialectFactory.getCountBySql(sqlToyContext, queryExecutor, sqlToyConfig,
getDataSource(extend.dataSource, sqlToyConfig));
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoCountQuery(sqlToyContext, dialectFactory, queryExecutor);
return result;
}
protected StoreResult executeStore(final String storeSqlOrKey, final Object[] inParamValues,
final Integer[] outParamsType, final Class resultType) {
return executeStore(storeSqlOrKey, inParamValues, outParamsType, resultType, null);
}
protected StoreResult executeStore(final String storeSqlOrKey, final Object[] inParamValues) {
return executeStore(storeSqlOrKey, inParamValues, null, null, null);
}
/**
* @todo 通用存储过程调用,一般数据库{?=call xxxStore(? in,? in,? out)} 针对oracle数据库只能{call
* xxxStore(? in,? in,? out)} 同时结果集必须通过OracleTypes.CURSOR out 参数返回
* 目前此方法只能返回一个结果集(集合类数据),可以返回多个非集合类数据,如果有特殊用法,则自行封装调用
* @param storeSqlOrKey 可以直接传call storeName (?,?) 也可以传xml中的存储过程sqlId
* @param inParamsValue
* @param outParamsType (可以为null)
* @param resultType VOClass,HashMap或null(表示二维List)
* @param dataSource
* @return
*/
protected StoreResult executeStore(final String storeSqlOrKey, final Object[] inParamsValue,
final Integer[] outParamsType, final Class resultType, final DataSource dataSource) {
SqlToyConfig sqlToyConfig = getSqlToyConfig(storeSqlOrKey, SqlType.search);
return dialectFactory.executeStore(sqlToyContext, sqlToyConfig, inParamsValue, outParamsType,
new Class[] { resultType }, false, getDataSource(dataSource, sqlToyConfig));
}
protected StoreResult executeMoreResultStore(final String storeSqlOrKey, final Object[] inParamsValue,
final Integer[] outParamsType, final Class[] resultTypes) {
SqlToyConfig sqlToyConfig = getSqlToyConfig(storeSqlOrKey, SqlType.search);
return dialectFactory.executeStore(sqlToyContext, sqlToyConfig, inParamsValue, outParamsType, resultTypes, true,
getDataSource(null, sqlToyConfig));
}
/**
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @return
* @see #getSingleValue(String, Map)
*/
@Deprecated
protected Object getSingleValue(final String sqlOrSqlId, final String[] paramsNamed, final Object[] paramsValue) {
return getSingleValue(sqlOrSqlId, paramsNamed, paramsValue, null);
}
protected Object getSingleValue(final String sqlOrSqlId, final Map paramsMap) {
Object queryResult = loadByQuery(new QueryExecutor(sqlOrSqlId, (paramsMap == null) ? MapKit.map() : paramsMap));
if (null != queryResult) {
return ((List) queryResult).get(0);
}
return null;
}
// add 2022-2-25
protected T getSingleValue(final String sqlOrSqlId, final Map paramsMap,
final Class resultType) {
if (resultType == null) {
throw new IllegalArgumentException("getSingleValue resultType 不能为null!");
}
Object value = getSingleValue(sqlOrSqlId, paramsMap);
if (value == null) {
return null;
}
try {
return (T) BeanUtil.convertType(value, DataType.getType(resultType), resultType.getTypeName());
} catch (Exception e) {
throw new DataAccessException("getSingleValue方法获取单个值失败:" + e.getMessage(), e);
}
}
/**
* @todo 返回单行单列值,如果结果集存在多条数据则返回null
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @param dataSource
* @return
*/
protected Object getSingleValue(final String sqlOrSqlId, final String[] paramsNamed, final Object[] paramsValue,
final DataSource dataSource) {
Object queryResult = loadByQuery(
new QueryExecutor(sqlOrSqlId, paramsNamed, paramsValue).dataSource(dataSource));
if (null != queryResult) {
return ((List) queryResult).get(0);
}
return null;
}
/**
* @todo 根据给定的对象中的主键值获取对象完整信息
* @param entity
* @return
*/
protected T load(final T entity) {
if (entity == null) {
return null;
}
EntityMeta entityMeta = this.getEntityMeta(entity.getClass());
if (SqlConfigParseUtils.isNamedQuery(entityMeta.getLoadSql(null))) {
return (T) loadBySql(entityMeta.getLoadSql(null), entity);
}
return load(entity, null, null);
}
/**
* @todo 提供锁定功能的加载
* @param entity
* @param lockMode
* @return
*/
protected T load(final T entity, final LockMode lockMode) {
return load(entity, lockMode, null);
}
/**
* @todo 根据主键值获取对应的记录信息
* @param entity
* @param lockMode
* @param dataSource
* @return
*/
protected T load(final T entity, final LockMode lockMode, final DataSource dataSource) {
return dialectFactory.load(sqlToyContext, entity, false, null, lockMode, getDataSource(dataSource));
}
/**
* @todo 指定需要级联加载的类型,通过主对象加载自身和相应的子对象集合
* @param entity
* @param lockMode
* @param cascadeTypes
* @return
*/
protected T loadCascade(T entity, LockMode lockMode, Class... cascadeTypes) {
if (entity == null) {
return null;
}
Class[] cascades = cascadeTypes;
// 当没有指定级联子类默认全部级联加载(update 2020-7-31 缺失了cascades.length == 0 判断)
if (cascades == null || cascades.length == 0) {
cascades = getEntityMeta(entity.getClass()).getCascadeTypes();
}
return dialectFactory.load(sqlToyContext, entity, false, cascades, lockMode, getDataSource(null));
}
/**
* @todo 批量根据实体对象的主键获取对象的详细信息
* @param entities
* @param lockMode
* @return
*/
protected List loadAll(final List entities, final LockMode lockMode) {
return dialectFactory.loadAll(sqlToyContext, entities, null, null, lockMode, null, getDataSource(null));
}
/**
* @TODO 根据id集合批量加载对象
* @param
* @param entityClass
* @param ids
* @return
*/
protected List loadByIds(final Class entityClass, Object... ids) {
return loadByIds(entityClass, null, ids);
}
/**
* @TODO 通过id集合批量加载对象
* @param
* @param entityClass
* @param lockMode
* @param ids
* @return
*/
protected List loadByIds(final Class entityClass, final LockMode lockMode,
Object... ids) {
if (entityClass == null || ids == null || ids.length == 0 || (ids.length == 1 && ids[0] == null)) {
throw new IllegalArgumentException("loadByIds操作:entityClass、主键值数据不能为空!");
}
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, true);
if (entityMeta.getIdArray().length != 1) {
throw new IllegalArgumentException("loadByIds操作只支持单主键POJO对象!");
}
Object[] realIds;
// 单个Collection,将List转为Array数组
if (ids.length == 1 && ids[0] instanceof Collection) {
realIds = ((Collection) ids[0]).toArray();
} else {
realIds = ids;
}
List entities = BeanUtil.wrapEntities(sqlToyContext.getTypeHandler(), entityMeta, entityClass, realIds);
return dialectFactory.loadAll(sqlToyContext, entities, null, null, lockMode, null, getDataSource(null));
}
protected List loadAllCascade(final List entities, final LockMode lockMode,
final Class... cascadeTypes) {
return loadAllCascade(entities, null, lockMode, cascadeTypes);
}
/**
* @todo 批量对象级联加载,指定级联加载的子表
* @param
* @param entities
* @param onlySubTable
* @param lockMode
* @param cascadeTypes
* @return
*/
protected List loadAllCascade(final List entities, final Boolean onlySubTable,
final LockMode lockMode, final Class... cascadeTypes) {
if (entities == null || entities.isEmpty()) {
return entities;
}
Class[] cascades = cascadeTypes;
if (cascades == null || cascades.length == 0) {
cascades = getEntityMeta(entities.get(0).getClass()).getCascadeTypes();
}
return dialectFactory.loadAll(sqlToyContext, entities, onlySubTable, cascades, lockMode, null,
getDataSource(null));
}
protected T loadBySql(final String sqlOrSqlId, final Map paramsMap, final Class resultType) {
return (T) loadByQuery(
new QueryExecutor(sqlOrSqlId, (paramsMap == null) ? MapKit.map() : paramsMap).resultType(resultType));
}
/**
* @todo 根据sql语句查询并返回单个VO对象(可指定自定义对象,sqltoy根据查询label跟对象的属性名称进行匹配映射)
* @param sqlOrSqlId
* @param paramNames
* @param paramValues
* @param resultType
* @return
*/
protected T loadBySql(final String sqlOrSqlId, final String[] paramNames, final Object[] paramValues,
final Class resultType) {
return (T) loadByQuery(new QueryExecutor(sqlOrSqlId, paramNames, paramValues).resultType(resultType));
}
/**
* @todo 解析sql中:named 属性到entity对象获取对应的属性值作为查询条件,并将查询结果以entity的class类型返回
* @param sqlOrSqlId
* @param entity
* @return
*/
protected T loadBySql(final String sqlOrSqlId, final T entity) {
return (T) loadByQuery(new QueryExecutor(sqlOrSqlId, entity));
}
protected T loadEntity(Class entityClass, EntityQuery entityQuery) {
List result = findEntity(entityClass, entityQuery);
if (result == null || result.isEmpty()) {
return null;
}
if (result.size() == 1) {
return result.get(0);
}
throw new IllegalArgumentException("loadEntity查询出:" + result.size() + " 条记录,不符合load查询单条记录的预期!");
}
/**
* TODO 通过构造QueyExecutor 提供更加灵活的参数传递方式,包括DataSource 比如:
* 1、new QueryExecutor(sql,entity).dataSource(dataSource)
* 2、new
* QueryExecutor(sql).names(paramNames).values(paramValues).resultType(resultType);
*
*
* @param queryExecutor
* @return
*/
protected Object loadByQuery(final QueryExecutor queryExecutor) {
QueryExecutorExtend extend = queryExecutor.getInnerModel();
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(extend.dataSource));
QueryResult result = dialectFactory.findByQuery(sqlToyContext, queryExecutor, sqlToyConfig, null,
getDataSource(extend.dataSource, sqlToyConfig));
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoQuery(sqlToyContext, dialectFactory, queryExecutor);
List rows = result.getRows();
if (rows == null || rows.isEmpty()) {
return null;
}
if (rows.size() == 1) {
return rows.get(0);
}
throw new IllegalArgumentException("loadByQuery查询出:" + rows.size() + " 条记录,不符合load查询单条记录的预期!");
}
/**
* @todo 执行无条件的sql语句,一般是一个修改、删除等操作,并返回修改的记录数量
* @param sqlOrSqlId
* @return
*/
protected Long executeSql(final String sqlOrSqlId) {
return executeSql(sqlOrSqlId, null, null, null, null);
}
/**
* @todo 解析sql中的参数名称,以此名称到entity中提取对应的值作为查询条件值执行sql
* @param sqlOrSqlId
* @param entity
* @return
*/
protected Long executeSql(final String sqlOrSqlId, final Serializable entity) {
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(sqlOrSqlId, SqlType.update, getDialect(dataSource),
entity);
return dialectFactory.executeSql(sqlToyContext, sqlToyConfig, new QueryExecutor(sqlOrSqlId, entity), null, null,
getDataSource(null, sqlToyConfig));
}
protected Long executeSql(final String sqlOrSqlId, final Map paramsMap) {
return executeSql(sqlOrSqlId, (Serializable) new IgnoreKeyCaseMap(paramsMap));
}
/**
* @todo 执行无返回结果的SQL(返回updateCount)
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @return
*/
protected Long executeSql(final String sqlOrSqlId, final String[] paramsNamed, final Object[] paramsValue) {
return executeSql(sqlOrSqlId, paramsNamed, paramsValue, null, null);
}
/**
* @todo 执行无返回结果的SQL(返回updateCount),根据autoCommit设置是否自动提交
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @param autoCommit 自动提交,默认可以填null
* @param dataSource
* @return
*/
protected Long executeSql(final String sqlOrSqlId, final String[] paramsNamed, final Object[] paramsValue,
final Boolean autoCommit, final DataSource dataSource) {
QueryExecutor query = new QueryExecutor(sqlOrSqlId).names(paramsNamed).values(paramsValue);
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(query, SqlType.update, getDialect(dataSource));
return dialectFactory.executeSql(sqlToyContext, sqlToyConfig, query, null, autoCommit,
getDataSource(dataSource, sqlToyConfig));
}
protected Long batchUpdate(final String sqlOrSqlId, final List dataSet, final Boolean autoCommit) {
// 例如sql 为:merge into table update set xxx=:param
// dataSet可以是VO List,可以根据属性自动映射到:param
return batchUpdate(sqlOrSqlId, dataSet, sqlToyContext.getBatchSize(), autoCommit, null);
}
/**
* @todo 通过jdbc方式批量插入数据,一般提供给数据采集时或插入临时表使用
* @param sqlOrSqlId
* @param dataSet 支持List、List(sql中?传参) ;List、List
* 形式(sql中:paramName传参)
* @param batchSize
* @param autoCommit 自动提交,默认可以填null
* @return
*/
protected Long batchUpdate(final String sqlOrSqlId, final List dataSet, final int batchSize,
final Boolean autoCommit) {
return batchUpdate(sqlOrSqlId, dataSet, batchSize, autoCommit, null);
}
/**
* @todo 批量执行sql修改或删除操作
* @param sqlOrSqlId
* @param dataSet 支持List、List(sql中?传参) ;List、List
* 形式(sql中:paramName传参)
* @param batchSize
* @param autoCommit
* @param dataSource
* @return
*/
protected Long batchUpdate(final String sqlOrSqlId, final List dataSet, final int batchSize,
final Boolean autoCommit, final DataSource dataSource) {
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(sqlOrSqlId, SqlType.update, getDialect(dataSource),
null);
return dialectFactory.batchUpdate(sqlToyContext, sqlToyConfig, dataSet, batchSize, null, null, null, autoCommit,
getDataSource(dataSource, sqlToyConfig));
}
protected boolean wrapTreeTableRoute(final TreeTableModel treeModel) {
return wrapTreeTableRoute(treeModel, null);
}
/**
* @todo 构造树形表的节点路径、节点层级、节点类别(是否叶子节点)
* @param treeModel
* @param dataSource
* @return
*/
protected boolean wrapTreeTableRoute(final TreeTableModel treeModel, final DataSource dataSource) {
return dialectFactory.wrapTreeTableRoute(sqlToyContext, treeModel, this.getDataSource(dataSource));
}
/**
* @todo 以entity对象的属性给sql中的:named 传参数,进行查询,并返回entityClass类型的集合
* @param sqlOrSqlId
* @param entity
* @return
*/
protected List findBySql(final String sqlOrSqlId, final T entity) {
return (List) findByQuery(new QueryExecutor(sqlOrSqlId, entity)).getRows();
}
protected List findBySql(final String sqlOrSqlId, final Map paramsMap,
final Class resultType) {
return (List) findByQuery(
new QueryExecutor(sqlOrSqlId, (paramsMap == null) ? MapKit.map() : paramsMap).resultType(resultType))
.getRows();
}
/**
* @TODO 查询集合
* @param
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @param resultType 分null(返回二维List)、voClass、HashMap.class、LinkedHashMap.class等
* @return
*/
protected List findBySql(final String sqlOrSqlId, final String[] paramsNamed, final Object[] paramsValue,
final Class resultType) {
QueryExecutor query = new QueryExecutor(sqlOrSqlId, paramsNamed, paramsValue);
if (resultType != null) {
query.resultType(resultType);
}
return (List) findByQuery(query).getRows();
}
/**
* @TODO 以queryExecutor 封装sql、条件、数据库源等进行集合查询
* @param queryExecutor (可动态设置数据源)
* @return
*/
protected QueryResult findByQuery(final QueryExecutor queryExecutor) {
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(queryExecutor.getInnerModel().dataSource));
QueryResult result = dialectFactory.findByQuery(sqlToyContext, queryExecutor, sqlToyConfig,
queryExecutor.getInnerModel().lockMode,
getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig));
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoQuery(sqlToyContext, dialectFactory, queryExecutor);
return result;
}
/**
* @TODO 按照流模式活动查询结果数据
* @param queryExecutor
* @param streamResultHandler
*/
protected void fetchStream(final QueryExecutor queryExecutor, final StreamResultHandler streamResultHandler) {
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(queryExecutor.getInnerModel().dataSource));
dialectFactory.fetchStream(sqlToyContext, queryExecutor, sqlToyConfig, streamResultHandler,
getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig));
}
/**
* @todo 以QueryExecutor 封装sql、参数等条件,实现分页查询
* @param page
* @param queryExecutor (可动态设置数据源)
* @return
*/
protected QueryResult findPageByQuery(final Page page, final QueryExecutor queryExecutor) {
String dialect = getDialect(queryExecutor.getInnerModel().dataSource);
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search, dialect);
// 自定义countsql
String countSql = queryExecutor.getInnerModel().countSql;
if (StringUtil.isNotBlank(countSql)) {
// 存在@include(sqlId) 或 @include(:sqlScript)
if (StringUtil.matches(countSql, SqlToyConstants.INCLUDE_PATTERN)) {
SqlToyConfig countSqlConfig = sqlToyContext.getSqlToyConfig(countSql, SqlType.search, dialect,
QueryExecutorBuilder.getParamValues(queryExecutor));
sqlToyConfig.setCountSql(countSqlConfig.getSql());
} else {
sqlToyConfig.setCountSql(countSql);
}
}
QueryResult result;
// 跳过查询总记录数量
if (page.getSkipQueryCount() != null && page.getSkipQueryCount()) {
result = dialectFactory.findSkipTotalCountPage(sqlToyContext, queryExecutor, sqlToyConfig, page.getPageNo(),
page.getPageSize(), getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig));
} else {
result = dialectFactory.findPage(sqlToyContext, queryExecutor, sqlToyConfig, page.getPageNo(),
page.getPageSize(), page.getOverPageToFirst(),
getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig));
}
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoPageQuery(sqlToyContext, dialectFactory, queryExecutor, page);
return result;
}
/**
* @todo 指定sql和参数名称以及名称对应的值和返回结果的类型(类型可以是java.util.HashMap),进行分页查询
* sql可以是一个具体的语句也可以是xml中定义的sqlId
* @param page
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @param resultType(null则返回List二维集合,HashMap.class:则返回List>)
* @return
*/
protected Page findPageBySql(final Page page, final String sqlOrSqlId, final String[] paramsNamed,
final Object[] paramsValue, Class resultType) {
QueryExecutor query = new QueryExecutor(sqlOrSqlId, paramsNamed, paramsValue);
if (resultType != null) {
query.resultType(resultType);
}
return (Page) findPageByQuery(page, query).getPageResult();
}
protected Page findPageBySql(final Page page, final String sqlOrSqlId, final T entity) {
return (Page) findPageByQuery(page, new QueryExecutor(sqlOrSqlId, entity)).getPageResult();
}
protected Page findPageBySql(final Page page, final String sqlOrSqlId, final Map paramsMap,
Class resultType) {
return (Page) findPageByQuery(page, new QueryExecutor(sqlOrSqlId, paramsMap).resultType(resultType))
.getPageResult();
}
protected List findTopBySql(final String sqlOrSqlId, final Map paramsMap,
final Class resultType, final double topSize) {
return (List) findTopByQuery(
new QueryExecutor(sqlOrSqlId, (paramsMap == null) ? MapKit.map() : paramsMap).resultType(resultType),
topSize).getRows();
}
/**
* @todo 取符合条件的结果前多少数据,topSize>1 则取整数返回记录数量,topSize<1 则按比例返回结果记录(topSize必须是大于0)
* @param sqlOrSqlId
* @param paramsNamed
* @param paramsValue
* @param resultType(null则返回List二维集合,HashMap.class:则返回List>)
* @param topSize >1
* 取整数部分,<1
* 则表示按比例获取
* @return
*/
protected List findTopBySql(final String sqlOrSqlId, final String[] paramsNamed, final Object[] paramsValue,
final Class resultType, final double topSize) {
return (List) findTopByQuery(new QueryExecutor(sqlOrSqlId, paramsNamed, paramsValue).resultType(resultType),
topSize).getRows();
}
protected List findTopBySql(final String sqlOrSqlId, final T entity,
final double topSize) {
return (List) findTopByQuery(new QueryExecutor(sqlOrSqlId, entity), topSize).getRows();
}
/**
* @TODO 以queryExecutor封装sql、条件参数、数据源等进行取top集合查询
* @param queryExecutor (可动态设置数据源)
* @param topSize
* @return
*/
protected QueryResult findTopByQuery(final QueryExecutor queryExecutor, final double topSize) {
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(queryExecutor.getInnerModel().dataSource));
QueryResult result = dialectFactory.findTop(sqlToyContext, queryExecutor, sqlToyConfig, topSize,
getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig));
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoTopQuery(sqlToyContext, dialectFactory, queryExecutor, topSize);
return result;
}
/**
* @todo 在符合条件的结果中随机提取多少条记录,randomCount>1 则取整数记录,randomCount<1 则按比例提取随机记录
* 如randomCount=0.05 总记录数为100,则随机取出5条记录
* @param queryExecutor (可动态设置数据源)
* @param randomCount
* @return
*/
protected QueryResult getRandomResult(final QueryExecutor queryExecutor, final double randomCount) {
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(queryExecutor.getInnerModel().dataSource));
QueryResult result = dialectFactory.getRandomResult(sqlToyContext, queryExecutor, sqlToyConfig, randomCount,
getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig));
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoRandomQuery(sqlToyContext, dialectFactory, queryExecutor, randomCount);
return result;
}
protected List getRandomResult(final String sqlOrSqlId, final Map paramsMap,
Class resultType, final double randomCount) {
return (List) getRandomResult(
new QueryExecutor(sqlOrSqlId, (paramsMap == null) ? MapKit.map() : paramsMap).resultType(resultType),
randomCount).getRows();
}
// resultType(null则返回List二维集合,HashMap.class:则返回List>)
protected List getRandomResult(final String sqlOrSqlId, final String[] paramsNamed,
final Object[] paramsValue, Class resultType, final double randomCount) {
return (List) getRandomResult(new QueryExecutor(sqlOrSqlId, paramsNamed, paramsValue).resultType(resultType),
randomCount).getRows();
}
protected void truncate(final Class entityClass, final Boolean autoCommit) {
if (null == entityClass) {
throw new IllegalArgumentException("entityClass is null,please check!");
}
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, false);
truncate(entityMeta.getSchemaTable(null, null), autoCommit, null);
}
/**
* @todo 快速删除表中的数据,autoCommit为null表示按照连接的默认值(如dbcp可以配置默认是否autoCommit)
* @param tableName
* @param autoCommit
* @param dataSource
*/
protected void truncate(final String tableName, final Boolean autoCommit, final DataSource dataSource) {
if (StringUtil.isBlank(tableName)) {
throw new IllegalArgumentException("truncate tableName is blank or null,please check!");
}
executeSql("truncate table ".concat(tableName), null, null, autoCommit, this.getDataSource(dataSource));
}
/**
* @todo 保存对象数据(返回插入的主键值),会针对对象的子集进行级联保存
* @param entity
* @return
*/
protected Object save(final Serializable entity) {
return save(entity, null);
}
/**
* @todo 指定数据库插入单个对象并返回主键值,会针对对象的子表集合数据进行级联保存
* @param entity
* @param dataSource
* @return
*/
protected Object save(final Serializable entity, final DataSource dataSource) {
return dialectFactory.save(sqlToyContext, entity, getDataSource(dataSource));
}
/**
* @todo 批量插入对象(会自动根据主键策略产生主键值,并填充对象集合),不做级联操作
* @param
* @param entities
* @return
*/
protected Long saveAll(final List entities) {
return this.saveAll(entities, null);
}
/**
* @todo 指定数据库进行批量插入
* @param
* @param entities
* @param dataSource
* @return
*/
protected Long saveAll(final List entities, final DataSource dataSource) {
return dialectFactory.saveAll(sqlToyContext, entities, sqlToyContext.getBatchSize(), null, null,
getDataSource(dataSource), null);
}
/**
* @todo 保存对象数据(返回插入的主键值),忽视已经存在的
* @param entities
* @return
*/
protected Long saveAllIgnoreExist(final List entities) {
return saveAllIgnoreExist(entities, null);
}
/**
* @todo 保存对象数据(返回插入的主键值),忽视已经存在的
* @param entities
* @param dataSource
* @return
*/
protected Long saveAllIgnoreExist(final List entities, final DataSource dataSource) {
return dialectFactory.saveAllIgnoreExist(sqlToyContext, entities, sqlToyContext.getBatchSize(), null, null,
getDataSource(dataSource), null);
}
/**
* @todo update对象(值为null的属性不修改,通过forceUpdateProps指定要进行强制修改属性)
* @param entity
* @param forceUpdateProps 强制修改的属性
* @return
*/
protected Long update(final Serializable entity, final String... forceUpdateProps) {
return this.update(entity, forceUpdateProps, null);
}
/**
* @todo 根据传入的对象,通过其主键值查询并修改其它属性的值
* @param entity
* @param forceUpdateProps
* @param dataSource
* @return
*/
protected Long update(final Serializable entity, final String[] forceUpdateProps, final DataSource dataSource) {
if (entity == null) {
logger.warn("update entity is null,please check!");
return 0L;
}
Class entityClass = entity.getClass();
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, true);
DataVersionConfig dataVersion = entityMeta.getDataVersion();
if (dataVersion != null) {
Object version = BeanUtil.getProperty(entity, dataVersion.getField());
if (version == null) {
throw new IllegalArgumentException("表:" + entityMeta.getTableName() + " 存在版本@DataVersion配置,属性:"
+ dataVersion.getField() + " 值不能为空!");
}
String where = "";
for (String field : entityMeta.getIdArray()) {
where = where.concat(field).concat("=:").concat(field).concat(" and ");
}
where = where.concat(dataVersion.getField()).concat("=:").concat(dataVersion.getField());
// 锁住id+version条件符合的记录
Serializable versionEntity = loadEntity(entity.getClass(), EntityQuery.create()
.select(entityMeta.getIdArray()).where(where).values(entity).lock(LockMode.UPGRADE_NOWAIT));
String verStr = version.toString();
if (versionEntity == null) {
throw new DataAccessException(
"表:" + entityMeta.getTableName() + " 数据版本:" + verStr + " 正在被其他用户修改或已经被更新!");
}
// 以日期开头
if (dataVersion.isStartDate()) {
String nowDate = DateUtil.formatDate(DateUtil.getNowTime(), DateUtil.FORMAT.DATE_8CHAR);
if (verStr.startsWith(nowDate)) {
verStr = nowDate + (Integer.parseInt(verStr.substring(8)) + 1);
} else {
verStr = nowDate + 1;
}
} else {
verStr = "" + (Integer.parseInt(verStr) + 1);
}
// 更新版本号
BeanUtil.setProperty(entity, dataVersion.getField(), verStr);
}
return dialectFactory.update(sqlToyContext, entity, forceUpdateProps, false, null, null,
getDataSource(dataSource));
}
/**
* @todo 修改对象,并通过指定级联的子对象做级联修改
* @param entity
* @param forceUpdateProps
* @param forceCascadeClasses (强制需要修改的子对象,当子集合数据为null,则进行清空或置为无效处理,否则则忽视对存量数据的处理)
* @param subTableForceUpdateProps
* @return
*/
protected Long updateCascade(final Serializable entity, final String[] forceUpdateProps,
final Class[] forceCascadeClasses, final HashMap subTableForceUpdateProps) {
return dialectFactory.update(sqlToyContext, entity, forceUpdateProps, true, forceCascadeClasses,
subTableForceUpdateProps, getDataSource(null));
}
/**
* @TODO 适用于库存台账、客户资金账等高并发强事务场景,一次数据库交互实现:1、锁查询;2、记录存在则修改;3、记录不存在则执行insert;4、返回修改或插入的记录信息,尽量不要使用identity、sequence主键
* @param
* @param entity
* @param updateRowHandler
* @param uniqueProps
* @param dataSource
* @return
*/
public T updateSaveFetch(final T entity, final UpdateRowHandler updateRowHandler,
final String[] uniqueProps, final DataSource dataSource) {
return (T) dialectFactory.updateSaveFetch(sqlToyContext, entity, updateRowHandler, uniqueProps,
getDataSource(dataSource));
}
/**
* @todo 深度更新实体对象数据,根据对象的属性值全部更新对应表的字段数据,不涉及级联修改
* @param entity
* @return
*/
protected Long updateDeeply(final Serializable entity) {
return this.updateDeeply(entity, null);
}
/**
* @todo 深度修改,即对象所有属性值都映射到数据库中,如果是null则数据库值被改为null
* @param entity
* @param dataSource
* @return
*/
protected Long updateDeeply(final Serializable entity, final DataSource dataSource) {
if (entity == null) {
logger.warn("updateDeeply entity is null,please check!");
return 0L;
}
EntityMeta entityMeta = getEntityMeta(entity.getClass());
return this.update(entity, (entityMeta == null) ? null : entityMeta.getRejectIdFieldArray(),
getDataSource(dataSource));
}
/**
* @todo 批量根据主键更新每条记录,通过forceUpdateProps设置强制要修改的属性
* @param
* @param entities
* @param forceUpdateProps
* @return
*/
protected Long updateAll(final List entities, final String... forceUpdateProps) {
return this.updateAll(entities, forceUpdateProps, null);
}
/**
* @todo 指定数据库,通过集合批量修改数据库记录
* @param
* @param entities
* @param forceUpdateProps
* @param dataSource
* @return
*/
protected Long updateAll(final List entities, final String[] forceUpdateProps,
final DataSource dataSource) {
return dialectFactory.updateAll(sqlToyContext, entities, sqlToyContext.getBatchSize(), null, forceUpdateProps,
null, null, getDataSource(dataSource), null);
}
/**
* @todo 批量深度修改(参见updateDeeply,直接将集合VO中的字段值修改到数据库中,未null则置null)
* @param
* @param entities
* @return
*/
protected Long updateAllDeeply(final List entities) {
return updateAllDeeply(entities, null);
}
/**
* @todo 指定数据源进行批量深度修改(对象属性值为null则设置表对应的字段为null)
* @param
* @param entities
* @param dataSource
* @return
*/
protected Long updateAllDeeply(final List entities, final DataSource dataSource) {
if (entities == null || entities.isEmpty()) {
logger.warn("updateAllDeeply List is null,please check!");
return 0L;
}
EntityMeta entityMeta = getEntityMeta(entities.get(0).getClass());
return updateAll(entities, (entityMeta == null) ? null : entityMeta.getRejectIdFieldArray(), null);
}
protected Long saveOrUpdate(final Serializable entity, final String... forceUpdateProps) {
return this.saveOrUpdate(entity, forceUpdateProps, null);
}
/**
* @todo 指定数据库,对对象进行保存或修改,forceUpdateProps:当修改操作时强制修改的属性
* @param entity
* @param forceUpdateProps
* @param dataSource
* @return
*/
protected Long saveOrUpdate(final Serializable entity, final String[] forceUpdateProps,
final DataSource dataSource) {
if (entity == null) {
logger.warn("saveOrUpdate: entity is null,please check!");
return 0L;
}
Class entityClass = entity.getClass();
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, false);
// 存在数据版本控制,如果主键和版本数据都有值,表示做更新操作
if (entityMeta.getDataVersion() != null && entityMeta.getIdArray() != null) {
String[] props = new String[entityMeta.getIdArray().length + 1];
System.arraycopy(entityMeta.getIdArray(), 0, props, 0, entityMeta.getIdArray().length);
props[props.length - 1] = entityMeta.getDataVersion().getField();
Object[] values = BeanUtil.reflectBeanToAry(entity, props);
boolean isUpdate = true;
for (int i = 0; i < props.length; i++) {
if (values[i] == null) {
isUpdate = false;
break;
}
}
// 全部有值,且版本字段值不为0表示是更新操作
if (isUpdate && !"0".equals(values[props.length - 1].toString())) {
return update(entity, forceUpdateProps, dataSource);
}
}
return dialectFactory.saveOrUpdate(sqlToyContext, entity, forceUpdateProps, getDataSource(dataSource));
}
/**
* @todo 批量保存或修改,并指定强迫修改的字段属性
* @param
* @param entities
* @param forceUpdateProps
* @return
*/
protected Long saveOrUpdateAll(final List entities, final String... forceUpdateProps) {
return this.saveOrUpdateAll(entities, forceUpdateProps, null);
}
/**
* @todo 批量保存或修改
* @param
* @param entities
* @param forceUpdateProps
* @param dataSource
* @return
*/
protected Long saveOrUpdateAll(final List entities, final String[] forceUpdateProps,
final DataSource dataSource) {
return dialectFactory.saveOrUpdateAll(sqlToyContext, entities, sqlToyContext.getBatchSize(), forceUpdateProps,
null, null, getDataSource(dataSource), null);
}
/**
* @todo 通过主键删除单条记录(会自动级联删除子表,根据数据库配置)
* @param entity
* @return
*/
protected Long delete(final Serializable entity) {
return dialectFactory.delete(sqlToyContext, entity, getDataSource(null));
}
protected Long delete(final Serializable entity, final DataSource dataSource) {
return dialectFactory.delete(sqlToyContext, entity, getDataSource(dataSource));
}
/**
* @TODO 提供单表简易查询进行删除操作(删除操作filters过滤无效)
* @param entityClass
* @param entityQuery
* @return
*/
protected Long deleteByQuery(Class entityClass, EntityQuery entityQuery) {
EntityQueryExtend innerModel = entityQuery.getInnerModel();
if (null == entityClass || null == entityQuery || StringUtil.isBlank(innerModel.where)
|| StringUtil.isBlank(innerModel.values)) {
throw new IllegalArgumentException("deleteByQuery entityClass、where、value 值不能为空!");
}
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, false);
// 做一个必要提示
if (!innerModel.paramFilters.isEmpty()) {
logger.warn("删除操作设置动态条件过滤是无效的,数据删除查询条件必须是精准的!");
}
String where = SqlUtil.convertFieldsToColumns(entityMeta, innerModel.where);
String sql = "delete from ".concat(entityMeta.getSchemaTable(null, null));
// 避开1=1
if (!where.replaceAll("\\s", "").equals("1=1")) {
sql = sql.concat(" where ").concat(where);
}
QueryExecutor queryExecutor = null;
// :named 模式
if (SqlConfigParseUtils.hasNamedParam(where) && StringUtil.isBlank(innerModel.names)) {
queryExecutor = new QueryExecutor(sql, (Serializable) innerModel.values[0]);
} else {
queryExecutor = new QueryExecutor(sql).names(innerModel.names).values(innerModel.values);
}
queryExecutor.getInnerModel().blankToNull = innerModel.blankToNull;
if (innerModel.paramFilters != null && innerModel.paramFilters.size() > 0) {
queryExecutor.getInnerModel().paramFilters.addAll(innerModel.paramFilters);
}
// 分库分表策略
setEntitySharding(queryExecutor, entityMeta);
// 为后续租户过滤提供判断依据(单表简单sql和对应的实体对象)
queryExecutor.getInnerModel().entityClass = entityClass;
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.delete,
getDialect(innerModel.dataSource));
sqlToyConfig.setSqlType(SqlType.delete);
return dialectFactory.executeSql(sqlToyContext, sqlToyConfig, queryExecutor, null, null,
getDataSource(innerModel.dataSource));
}
protected Long deleteAll(final List entities) {
return deleteAll(entities, null);
}
/**
* @todo 批量删除数据
* @param
* @param entities
* @param dataSource
* @return
*/
protected Long deleteAll(final List entities, final DataSource dataSource) {
return dialectFactory.deleteAll(sqlToyContext, entities, sqlToyContext.getBatchSize(), null,
getDataSource(dataSource), null);
}
/**
* @TODO 提供单一主键对象的批量快速删除调用方法
* @param entityClass
* @param ids
* @return
*/
protected Long deleteByIds(Class entityClass, Object... ids) {
if (entityClass == null || ids == null || ids.length == 0 || (ids.length == 1 && ids[0] == null)) {
throw new IllegalArgumentException("deleteByIds操作:entityClass参数、主键数据不能为空!");
}
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, true);
Object[] realIds;
// 单个List,将List转为Array数组
if (ids.length == 1 && ids[0] instanceof Collection) {
realIds = ((Collection) ids[0]).toArray();
} else {
realIds = ids;
}
// 为什么统一转成对象集合?便于后面存在分库分表场景、ids超过1000条等场景
List entities = BeanUtil.wrapEntities(sqlToyContext.getTypeHandler(), entityMeta, entityClass, realIds);
return this.deleteAll(entities, null);
}
/**
* @todo 锁定记录查询,并对记录进行修改,最后将结果返回
* @param queryExecutor
* @param updateRowHandler
* @return
*/
protected List updateFetch(final QueryExecutor queryExecutor, final UpdateRowHandler updateRowHandler) {
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(queryExecutor.getInnerModel().dataSource));
return dialectFactory.updateFetch(sqlToyContext, queryExecutor, sqlToyConfig, updateRowHandler,
getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig)).getRows();
}
/**
* @todo 获取对象信息(对应的表以及字段、主键策略等等的信息)
* @param entityClass
* @return
*/
protected EntityMeta getEntityMeta(Class entityClass) {
return sqlToyContext.getEntityMeta(entityClass);
}
/**
* @todo 获取sqltoy配置的批处理每批记录量(默认为50)
* @return
*/
protected int getBatchSize() {
return sqlToyContext.getBatchSize();
}
/**
* @todo 协助完成对对象集合的属性批量赋予相应数值
* @param names
* @return
*/
protected BeanWrapper wrapBeanProps(String... names) {
return BeanWrapper.create().names(names);
}
/**
* @todo 手工提交数据库操作,只提供当前DataSource提交
*/
protected void flush() {
flush(null);
}
/**
* @todo 手工提交数据库操作,只提供当前DataSource提交
* @param dataSource
*/
protected void flush(DataSource dataSource) {
DataSourceUtils.processDataSource(sqlToyContext, getDataSource(dataSource), new DataSourceCallbackHandler() {
@Override
public void doConnection(Connection conn, Integer dbType, String dialect) throws Exception {
if (!conn.isClosed()) {
conn.commit();
}
}
});
}
/**
* @todo 产生ID(可以指定增量范围,当一个表里面涉及多个业务主键时,sqltoy在配置层面只支持单个,但开发者可以调用此方法自行获取后赋值)
* @param signature 唯一标识符号
* @param increment 唯一标识符号,默认设置为1
* @return
*/
protected long generateBizId(String signature, int increment) {
if (StringUtil.isBlank(signature)) {
throw new IllegalArgumentException("signature 必须不能为空,请正确指定业务标志符号!");
}
if (distributeIdGenerator == null) {
try {
distributeIdGenerator = (DistributeIdGenerator) Class
.forName(sqlToyContext.getDistributeIdGeneratorClass()).getDeclaredConstructor().newInstance();
distributeIdGenerator.initialize(sqlToyContext.getAppContext());
} catch (Exception e) {
e.printStackTrace();
throw new DataAccessException("实例化分布式id产生器失败:" + e.getMessage());
}
}
return distributeIdGenerator.generateId(signature, increment, null);
}
/**
* @todo 根据实体对象对应的POJO配置的业务主键策略,提取对象的属性值产生业务主键
* @param entity
* @return
*/
protected String generateBizId(Serializable entity) {
EntityMeta entityMeta = getEntityMeta(entity.getClass());
if (entityMeta == null || !entityMeta.isHasBizIdConfig()) {
throw new IllegalArgumentException(
StringUtil.fillArgs("对象:{},没有配置业务主键生成策略,请检查POJO 的业务主键配置!", entity.getClass().getName()));
}
String businessIdType = entityMeta.getColumnJavaType(entityMeta.getBusinessIdField());
Integer[] relatedColumn = entityMeta.getBizIdRelatedColIndex();
Object[] fullParamValues = BeanUtil.reflectBeanToAry(entity, entityMeta.getFieldsArray());
// 提取关联属性的值
Object[] relatedColValue = null;
if (relatedColumn != null) {
relatedColValue = new Object[relatedColumn.length];
for (int meter = 0; meter < relatedColumn.length; meter++) {
relatedColValue[meter] = fullParamValues[relatedColumn[meter]];
if (relatedColValue[meter] == null) {
throw new IllegalArgumentException("对象:" + entity.getClass().getName() + " 生成业务主键依赖的关联字段:"
+ relatedColumn[meter] + " 值为null!");
}
}
}
IdGenerator idGenerator = (entityMeta.getBusinessIdGenerator() == null) ? entityMeta.getIdGenerator()
: entityMeta.getBusinessIdGenerator();
return idGenerator.getId(entityMeta.getTableName(), entityMeta.getBizIdSignature(),
entityMeta.getBizIdRelatedColumns(), relatedColValue, new Date(), businessIdType,
entityMeta.getBizIdLength(), entityMeta.getBizIdSequenceSize()).toString();
}
/**
* @TODO 根据指定的表名、业务码,业务码的属性和值map,动态获取业务主键值 例如:generateBizId("sag_test",
* "HW@case(orderType,SALE,SC,BUY,PO)@day(yyMMdd)",
* MapKit.map("orderType", "SALE"), null, 12, 2);
* @param tableName
* @param signature 一个表达式字符串,支持@case(name,value1,then1,val2,then2)
* 和 @day(yyMMdd)或@day(yyyyMMdd)、@substr(name,start,length)
* 等
* @param keyValues
* @param bizDate 在signature为空时生效
* @param length
* @param sequenceSize
* @return
*/
protected String generateBizId(String tableName, String signature, Map keyValues, LocalDate bizDate,
int length, int sequenceSize) {
if (distributeIdGenerator == null) {
try {
distributeIdGenerator = (DistributeIdGenerator) Class
.forName(sqlToyContext.getDistributeIdGeneratorClass()).getDeclaredConstructor().newInstance();
distributeIdGenerator.initialize(sqlToyContext.getAppContext());
} catch (Exception e) {
e.printStackTrace();
throw new DataAccessException("实例化分布式id产生器失败:" + e.getMessage());
}
}
return IdUtil.getId(distributeIdGenerator, tableName, signature, keyValues, bizDate, length, sequenceSize);
}
/**
* @todo 获取所有缓存的名称
* @return
*/
protected Set getCacheNames() {
return this.sqlToyContext.getTranslateManager().getCacheNames();
}
/**
* @todo 判断缓存是否存在
* @param cacheName
* @return
*/
protected boolean existCache(String cacheName) {
return this.sqlToyContext.getTranslateManager().existCache(cacheName);
}
/**
* @TODO 将缓存数据以对象形式获取
* @param
* @param cacheName
* @param cacheType 如是数据字典,则传入字典类型否则为null即可
* @param reusltType
* @return
*/
protected List getTranslateCache(String cacheName, String cacheType, Class reusltType) {
TranslateConfigModel translateConfig = sqlToyContext.getTranslateManager().getCacheConfig(cacheName);
if (null == translateConfig) {
throw new DataAccessException("缓存翻译中对应的缓存:" + cacheName + " 没有定义,请正确检查配置!");
}
HashMap cacheData = sqlToyContext.getTranslateManager().getCacheData(cacheName, cacheType);
if (cacheData.isEmpty()) {
return new ArrayList();
}
if (null == reusltType || reusltType == Array.class) {
return new ArrayList(cacheData.values());
}
String[] props = translateConfig.getProperties();
// 注意直接sql定义的缓存,框架会自动获取label
if (props == null || props.length == 0) {
throw new DataAccessException("缓存翻译中的缓存:[" + cacheName + "]没有正确定义properties属性,无法映射到VO/POJO/Map对象!");
}
// 转map类型
if (reusltType == Map.class || reusltType == HashMap.class || reusltType == LinkedHashMap.class
|| reusltType == IgnoreKeyCaseMap.class || reusltType == IgnoreCaseLinkedMap.class) {
List result = new ArrayList();
Iterator iter = cacheData.values().iterator();
Object[] row;
int mapType = 1;
if (reusltType == LinkedHashMap.class) {
mapType = 2;
} else if (reusltType == IgnoreKeyCaseMap.class) {
mapType = 3;
} else if (reusltType == IgnoreCaseLinkedMap.class) {
mapType = 4;
}
while (iter.hasNext()) {
Map map;
if (mapType == 2) {
map = new LinkedHashMap();
} else if (mapType == 3) {
map = new IgnoreKeyCaseMap();
} else if (mapType == 4) {
map = new IgnoreCaseLinkedMap();
} else {
map = new HashMap();
}
row = iter.next();
for (int i = 0; i < props.length; i++) {
map.put(props[i], row[i]);
}
result.add(map);
}
return result;
}
return (List) BeanUtil.reflectListToBean(sqlToyContext.getTypeHandler(), cacheData.values(), props,
reusltType);
}
/**
* @todo 获取缓存数据
* @param cacheName
* @param cacheType
* @return
*/
protected HashMap getTranslateCache(String cacheName, String cacheType) {
return sqlToyContext.getTranslateManager().getCacheData(cacheName, cacheType);
}
/**
* @see cacheMatchKeys(CacheMatchFilter cacheMatchFilter, String...
* matchRegexes)
* @param matchRegex
* @param cacheMatchFilter
* @return
*/
@Deprecated
protected String[] cacheMatchKeys(String matchRegex, CacheMatchFilter cacheMatchFilter) {
return cacheMatchKeys(cacheMatchFilter, matchRegex);
}
/**
* @TODO 通过缓存匹配名称并返回key集合(类似数据库中的like)便于后续进行精准匹配
* @param cacheMatchFilter 例如:
* CacheMatchFilter.create().cacheName("staffIdNameCache")
* @param cacheMatchFilter 如: 页面传过来的员工名称、客户名称等,反查对应的员工id和客户id
* @param matchRegexes
* @return
*/
protected String[] cacheMatchKeys(CacheMatchFilter cacheMatchFilter, String... matchRegexes) {
if (cacheMatchFilter == null || StringUtil.isBlank(cacheMatchFilter.getCacheFilterArgs().cacheName)
|| matchRegexes == null || matchRegexes.length == 0) {
throw new IllegalArgumentException("缓存反向名称匹配key必须要提供cacheName和matchRegex值!");
}
CacheMatchExtend extendArgs = cacheMatchFilter.getCacheFilterArgs();
// 获取缓存数据
HashMap cacheDatas = getTranslateCache(extendArgs.cacheName, extendArgs.cacheType);
if (cacheDatas == null || cacheDatas.isEmpty()) {
logger.error("缓存cacheName={},cacheType={} 没有数据,cacheMatchKeys异常,请检查!", extendArgs.cacheName,
extendArgs.cacheType);
return new String[] {};
}
// 名称匹配在缓存的哪几列(正常1列,但部分场景要求:名称、别名 匹配等)
int[] nameIndexes = extendArgs.matchIndexs;
// 将传递匹配条件转小写
List matchLowAry = new ArrayList();
for (String str : matchRegexes) {
matchLowAry.add(str.toLowerCase().trim());
}
// 缓存key值列
int cacheKeyIndex = extendArgs.cacheKeyIndex;
// 最大允许匹配数量,缓存匹配一般用于in (?,?)形式的查询,in 参数有数量限制
int maxLimit = extendArgs.matchSize;
// 匹配到的key集合
Set matchedKeys = new HashSet();
String keyCode;
String matchStr;
String[] matchWords;
Object compareValue;
// 是否优先判断相等
boolean priorMatchEqual = extendArgs.priorMatchEqual;
boolean hasFilter = (extendArgs.cacheFilter == null) ? false : true;
boolean include = true;
// 优先匹配名称相同,名称相同直接剔除掉对比参数不再进行后续匹配
if (priorMatchEqual) {
String keyLow;
for (Object[] row : cacheDatas.values()) {
keyCode = row[cacheKeyIndex].toString();
include = true;
if (hasFilter) {
include = extendArgs.cacheFilter.doFilter(row);
}
if (include) {
keyLow = keyCode.toLowerCase();
skipLoop: for (int i = 0; i < matchLowAry.size(); i++) {
matchStr = matchLowAry.get(i);
// 模糊查询条件直接就跟key 相同
if (matchStr.equals(keyLow)) {
matchedKeys.add(keyCode);
// 剔除
matchLowAry.remove(i);
break;
}
// 名称相同
for (int index : nameIndexes) {
compareValue = row[index];
if (compareValue != null && compareValue.toString().toLowerCase().equals(matchStr)) {
matchedKeys.add(keyCode);
// 剔除
matchLowAry.remove(i);
break skipLoop;
}
}
}
// 完全匹配到相等、匹配量到最大量
if (matchLowAry.isEmpty() || matchedKeys.size() >= maxLimit) {
break;
}
}
}
}
// 排除相等优先后,进行模糊like 匹配
if (!matchLowAry.isEmpty() && matchedKeys.size() < maxLimit) {
int likeArgSize = matchLowAry.size();
// 将匹配参数切割成分词数组
List paramsMatchWords = new ArrayList();
for (int i = 0; i < likeArgSize; i++) {
paramsMatchWords.add(matchLowAry.get(i).split("\\s+"));
}
for (Object[] row : cacheDatas.values()) {
keyCode = row[cacheKeyIndex].toString();
include = true;
// 已经存在无需再比较
if (matchedKeys.contains(keyCode)) {
include = false;
}
// 对缓存进行过滤(比如过滤本人授权访问机构下面的员工或当期状态为生效的员工)
if (hasFilter && include) {
include = extendArgs.cacheFilter.doFilter(row);
}
if (include) {
skipLoop: for (int i = 0; i < likeArgSize; i++) {
// like 匹配
matchWords = paramsMatchWords.get(i);
for (int index : nameIndexes) {
compareValue = row[index];
if (compareValue != null
&& StringUtil.like(compareValue.toString().toLowerCase(), matchWords)) {
matchedKeys.add(keyCode);
break skipLoop;
}
}
}
// 超出阈值跳出
if (matchedKeys.size() >= maxLimit) {
break;
}
}
}
}
// 没有从缓存匹配到,返回自身
if (matchedKeys.isEmpty() && extendArgs.unMatchedReturnSelf) {
return matchRegexes;
}
String[] result = new String[matchedKeys.size()];
matchedKeys.toArray(result);
return result;
}
/**
* @todo 利用sqltoy的translate缓存,通过显式调用对集合数据的列进行翻译
* @param dataSet 要翻译的数据集合
* @param cacheName 缓存名称
* @param cacheType 缓存分类(如字典分类),非分类型的填null
* @param cacheNameIndex 缓存名称对应的列,默认为1(null也表示1)
* @param translateHandler 2个方法:getKey(Object row),setName(Object row,String
* name)
*/
protected void translate(Collection dataSet, String cacheName, String cacheType, Integer cacheNameIndex,
TranslateHandler translateHandler) {
// 数据以及合法性校验
if (dataSet == null || dataSet.isEmpty()) {
return;
}
if (cacheName == null) {
throw new IllegalArgumentException("缓存名称不能为空!");
}
if (translateHandler == null) {
throw new IllegalArgumentException("缓存翻译行取key和设置name的反调函数不能为null!");
}
// 获取缓存,框架会自动判断null并实现缓存数据的加载和更新检测
final HashMap cache = getTranslateCache(cacheName, cacheType);
if (cache == null || cache.isEmpty()) {
return;
}
Iterator iter = dataSet.iterator();
Object row;
Object key;
Object name;
// 默认名称字段列为1
int cacheIndex = (cacheNameIndex == null) ? 1 : cacheNameIndex.intValue();
Object[] keyRow;
// 循环获取行数据
while (iter.hasNext()) {
row = iter.next();
if (row != null) {
// 反调获取需要翻译的key
key = translateHandler.getKey(row);
if (key != null) {
keyRow = cache.get(key.toString());
// 从缓存中获取对应的名称
name = (keyRow == null) ? null : keyRow[cacheIndex];
// 反调设置行数据中具体列或属性翻译后的名称
translateHandler.setName(row, (name == null) ? "" : name.toString());
}
}
}
}
/**
* @TODO 提供针对单表简易快捷查询 EntityQuery.where("#[name like ?]#[and status in
* (?)]").values(new Object[]{xxx,xxx})
* @param
* @param entityClass
* @param entityQuery
* @return
*/
protected List findEntity(Class entityClass, EntityQuery entityQuery) {
return (List) findEntity(entityClass, entityQuery, entityClass);
}
protected List findEntity(Class entityClass, EntityQuery entityQuery, Class resultType) {
if (null == entityClass) {
throw new IllegalArgumentException("findEntityList entityClass值不能为空!");
}
return (List) findEntityBase(entityClass, null, (entityQuery == null) ? EntityQuery.create() : entityQuery,
resultType, false);
}
/**
* @TODO 提供针对单表简易快捷分页查询 EntityQuery.where("#[name like ?]#[and status in
* (?)]").values(new Object[]{xxx,xxx})
* @param
* @param page
* @param entityClass
* @param entityQuery
* @return
*/
protected Page findPageEntity(Page page, Class entityClass, EntityQuery entityQuery) {
return (Page) findPageEntity(page, entityClass, entityQuery, entityClass);
}
protected Page findPageEntity(Page page, Class entityClass, EntityQuery entityQuery, Class resultType) {
if (null == entityClass || null == page) {
throw new IllegalArgumentException("findPageEntity entityClass、page值不能为空!");
}
return (Page) findEntityBase(entityClass, page, (entityQuery == null) ? EntityQuery.create() : entityQuery,
resultType, false);
}
/**
* @TODO 提供findEntity的基础实现,供对外接口包装,额外开放了resultClass的自定义功能
* @param entityClass
* @param page 如分页查询则需指定,非分页则传null
* @param entityQuery
* @param resultClass 指定返回结果类型
* @param isCount
* @return
*/
private Object findEntityBase(Class entityClass, Page page, EntityQuery entityQuery, Class resultClass,
boolean isCount) {
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, false);
EntityQueryExtend innerModel = entityQuery.getInnerModel();
String translateFields = "";
// 将缓存翻译对应的查询补充到select column 上,形成select keyColumn as viewColumn 模式
if (!innerModel.translates.isEmpty()) {
Iterator iter = innerModel.translates.values().iterator();
String keyColumn;
TranslateExtend extend;
while (iter.hasNext()) {
extend = iter.next().getExtend();
// 将java模式的字段名称转化为数据库字段名称
keyColumn = entityMeta.getColumnName(extend.keyColumn);
if (keyColumn == null) {
keyColumn = extend.keyColumn;
}
// 保留字处理
keyColumn = ReservedWordsUtil.convertWord(keyColumn, null);
translateFields = translateFields.concat(",").concat(keyColumn).concat(" as ").concat(extend.column);
}
}
// 将notSelect构造成select,形成统一处理机制
String[] selectFieldAry = null;
Set notSelect = innerModel.notSelectFields;
if (notSelect != null) {
List selectFields = new ArrayList();
for (String field : entityMeta.getFieldsArray()) {
if (!notSelect.contains(field.toLowerCase())) {
selectFields.add(field);
}
}
if (selectFields.size() > 0) {
selectFieldAry = new String[selectFields.size()];
selectFields.toArray(selectFieldAry);
}
} else {
selectFieldAry = innerModel.fields;
}
// 指定的查询字段
String fields = "";
if (selectFieldAry != null && selectFieldAry.length > 0) {
int index = 0;
String colName;
HashSet cols = new HashSet();
boolean notAllPureField = false;
for (String field : selectFieldAry) {
// 去除重复字段
if (!cols.contains(field)) {
colName = entityMeta.getColumnName(field);
// 非表字段对应pojo的属性名称
if (colName == null) {
colName = field;
// 非字段名称
if (!entityMeta.getColumnFieldMap().containsKey(colName.toLowerCase())) {
notAllPureField = true;
} else {
// 保留字处理
colName = ReservedWordsUtil.convertWord(colName, null);
}
} else {
// 保留字处理
colName = ReservedWordsUtil.convertWord(colName, null);
}
if (index > 0) {
fields = fields.concat(",");
}
fields = fields.concat(colName);
index++;
cols.add(field);
}
}
// select 字段中可能存在max(field)或field as xxx等非字段形式
if (notAllPureField) {
fields = SqlUtil.convertFieldsToColumns(entityMeta, fields);
}
} else {
fields = entityMeta.getAllColumnNames();
}
String sql = "select ".concat((innerModel.distinct) ? " distinct " : "").concat(fields).concat(translateFields)
.concat(" from ").concat(entityMeta.getSchemaTable(null, null));
// where条件
String where = "";
// 动态组织where 后面的条件语句,此功能并不建议使用,where 一般需要指定明确条件
if (StringUtil.isBlank(innerModel.where)) {
if (innerModel.values != null && innerModel.values.length > 0) {
where = SqlUtil.wrapWhere(entityMeta);
}
} else {
where = SqlUtil.convertFieldsToColumns(entityMeta, innerModel.where);
}
if (StringUtil.isNotBlank(where)) {
sql = sql.concat(" where ").concat(where);
}
// 分组和having
if (StringUtil.isNotBlank(innerModel.groupBy)) {
sql = sql.concat(" group by ").concat(SqlUtil.convertFieldsToColumns(entityMeta, innerModel.groupBy));
if (StringUtil.isNotBlank(innerModel.having)) {
sql = sql.concat(" having ").concat(SqlUtil.convertFieldsToColumns(entityMeta, innerModel.having));
}
}
// 处理order by 排序
if (!innerModel.orderBy.isEmpty()) {
sql = sql.concat(" order by ");
Iterator> iter = innerModel.orderBy.entrySet().iterator();
Entry entry;
String columnName;
int index = 0;
while (iter.hasNext()) {
entry = iter.next();
columnName = entityMeta.getColumnName(entry.getKey());
if (columnName == null) {
columnName = entry.getKey();
}
// 保留字处理
columnName = ReservedWordsUtil.convertWord(columnName, null);
if (index > 0) {
sql = sql.concat(",");
}
// entry.getValue() is order way,like: desc or " "
sql = sql.concat(columnName).concat(entry.getValue());
index++;
}
}
QueryExecutor queryExecutor;
Class resultType = (resultClass == null) ? entityClass : resultClass;
// :named 模式(named模式参数值必须存在)
if (SqlConfigParseUtils.hasNamedParam(where) && StringUtil.isBlank(innerModel.names)) {
queryExecutor = new QueryExecutor(sql,
(innerModel.values == null || innerModel.values.length == 0) ? null
: (Serializable) innerModel.values[0])
.resultType(resultType).dataSource(getDataSource(innerModel.dataSource))
.fetchSize(innerModel.fetchSize).maxRows(innerModel.maxRows);
} else {
queryExecutor = new QueryExecutor(sql).names(innerModel.names).values(innerModel.values)
.resultType(resultType).dataSource(getDataSource(innerModel.dataSource))
.fetchSize(innerModel.fetchSize).maxRows(innerModel.maxRows);
}
// 设置是否空白转null
queryExecutor.getInnerModel().blankToNull = innerModel.blankToNull;
// 为后续租户过滤提供判断依据(单表简单sql和对应的实体对象)
queryExecutor.getInnerModel().entityClass = entityClass;
// 设置额外的缓存翻译
if (!innerModel.translates.isEmpty()) {
queryExecutor.getInnerModel().translates.putAll(innerModel.translates);
}
// 设置额外的参数条件过滤
if (!innerModel.paramFilters.isEmpty()) {
queryExecutor.getInnerModel().paramFilters.addAll(innerModel.paramFilters);
}
// 设置安全脱敏
if (!innerModel.secureMask.isEmpty()) {
queryExecutor.getInnerModel().secureMask.putAll(innerModel.secureMask);
}
// 是否输出sql
if (innerModel.showSql != null) {
queryExecutor.showSql(innerModel.showSql);
}
// 设置分页优化
queryExecutor.getInnerModel().pageOptimize = innerModel.pageOptimize;
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.search,
getDialect(queryExecutor.getInnerModel().dataSource));
// 加密字段,查询时解密
if (entityMeta.getSecureColumns() != null) {
sqlToyConfig.setDecryptColumns(entityMeta.getSecureColumns());
}
// 分库分表策略
setEntitySharding(queryExecutor, entityMeta);
if (innerModel.dbSharding != null) {
queryExecutor.getInnerModel().dbSharding = innerModel.dbSharding;
}
if (innerModel.tableSharding != null) {
ShardingStrategyConfig shardingConfig = innerModel.tableSharding;
// 补充表名称
shardingConfig.setTables(new String[] { entityMeta.getTableName() });
List tableShardings = new ArrayList();
tableShardings.add(shardingConfig);
queryExecutor.getInnerModel().tableShardings = tableShardings;
}
DataSource realDataSource = getDataSource(queryExecutor.getInnerModel().dataSource, sqlToyConfig);
Object result;
// 取count数量
if (isCount) {
result = dialectFactory.getCountBySql(sqlToyContext, queryExecutor, sqlToyConfig, realDataSource);
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoCountQuery(sqlToyContext, dialectFactory, queryExecutor);
return result;
}
// 非分页
if (page == null) {
// 取top
if (innerModel.pickType == 0) {
result = dialectFactory
.findTop(sqlToyContext, queryExecutor, sqlToyConfig, innerModel.pickSize, realDataSource)
.getRows();
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoTopQuery(sqlToyContext, dialectFactory, queryExecutor, innerModel.pickSize);
} // 取随机记录
else if (innerModel.pickType == 1) {
result = dialectFactory.getRandomResult(sqlToyContext, queryExecutor, sqlToyConfig, innerModel.pickSize,
realDataSource).getRows();
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoRandomQuery(sqlToyContext, dialectFactory, queryExecutor, innerModel.pickSize);
} else {
result = dialectFactory
.findByQuery(sqlToyContext, queryExecutor, sqlToyConfig, innerModel.lockMode, realDataSource)
.getRows();
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoQuery(sqlToyContext, dialectFactory, queryExecutor);
}
return result;
} else {
// 跳过总记录数形式的分页
if (page.getSkipQueryCount()) {
result = dialectFactory.findSkipTotalCountPage(sqlToyContext, queryExecutor, sqlToyConfig,
page.getPageNo(), page.getPageSize(), realDataSource).getPageResult();
} else {
result = dialectFactory.findPage(sqlToyContext, queryExecutor, sqlToyConfig, page.getPageNo(),
page.getPageSize(), page.getOverPageToFirst(), realDataSource).getPageResult();
}
// 产品化场景,适配其他数据库验证查询(仅仅在设置了redoDataSources时生效)
CrossDbAdapter.redoPageQuery(sqlToyContext, dialectFactory, queryExecutor, page);
return result;
}
}
/**
* @TODO 针对单表对象查询进行更新操作(update和delete 操作filters过滤是无效的,必须是精准的条件参数)
* @param entityClass
* @param entityUpdate
* @update 2021-12-23 支持update table set field=field+1等计算模式
* @return
*/
protected Long updateByQuery(Class entityClass, EntityUpdate entityUpdate) {
if (null == entityClass || null == entityUpdate || StringUtil.isBlank(entityUpdate.getInnerModel().where)
|| StringUtil.isBlank(entityUpdate.getInnerModel().values)
|| entityUpdate.getInnerModel().updateValues.isEmpty()) {
throw new IllegalArgumentException("updateByQuery: entityClass、where条件、条件值value、变更值setValues不能为空!");
}
EntityMeta entityMeta = getEntityMeta(entityClass);
validEntity(entityMeta, entityClass, false);
EntityUpdateExtend innerModel = entityUpdate.getInnerModel();
// 过滤无效set(field),即校验field不是数据库实际字段(@Column对应的属性)
IgnoreCaseLinkedMap realUpdateValues = wrapRealUpdateValues(innerModel.updateValues, entityMeta,
entityClass, innerModel.skipNotExistColumn);
boolean isName = SqlConfigParseUtils.hasNamedParam(innerModel.where);
Object[] values = innerModel.values;
String[] paramNames = null;
String where = innerModel.where;
int valueSize = (values == null) ? 0 : values.length;
// 重新通过对象反射获取参数条件的值
if (isName) {
// 校验必须是dto、map形式传参数
if (values.length > 1) {
throw new IllegalArgumentException("updateByQuery: where条件采用:paramName形式传参,values只能传递单个VO或Map对象!");
}
paramNames = SqlConfigParseUtils.getSqlParamsName(where, false);
values = BeanUtil.reflectBeanToAry(values[0], paramNames);
// 重新设置值数组的长度
valueSize = values.length;
} else {
int paramCnt = DialectUtils.getParamsCount(where);
if (paramCnt == 1 && StringUtil.matches(where, SqlConfigParseUtils.IN_PATTERN) && valueSize > 1) {
values = new Object[] { values };
valueSize = 1;
}
if (paramCnt != valueSize) {
throw new IllegalArgumentException("updateByQuery: where语句中的?数量跟对应values 数组长度不一致,请检查!");
}
}
// 处理where 中写的java 字段名称为数据库表字段名称
where = SqlUtil.convertFieldsToColumns(entityMeta, where);
StringBuilder sql = new StringBuilder();
sql.append("update ").append(entityMeta.getSchemaTable(null, null)).append(" set ");
// 对统一更新字段做处理
IUnifyFieldsHandler unifyHandler = getSqlToyContext().getUnifyFieldsHandler();
if (unifyHandler != null) {
Map updateFields = SqlToyThreadDataHolder.useUnifyFields()
? unifyHandler.updateUnifyFields()
: null;
if (updateFields != null && !updateFields.isEmpty()) {
Iterator> updateIter = updateFields.entrySet().iterator();
String columnName;
Entry entry;
while (updateIter.hasNext()) {
entry = updateIter.next();
columnName = entityMeta.getColumnName(entry.getKey());
// 是数据库表的字段
if (columnName != null) {
// 是否已经主动update
if (realUpdateValues.containsKey(entry.getKey()) || realUpdateValues.containsKey(columnName)) {
// 存在强制更新
if (unifyHandler.forceUpdateFields() != null
&& unifyHandler.forceUpdateFields().contains(entry.getKey())) {
// 覆盖主动设置的值
// 以表字段名称模式设置的值
if (realUpdateValues.containsKey(columnName)) {
realUpdateValues.put(columnName, entry.getValue());
} else {
// 属性名称设置的值
realUpdateValues.put(entry.getKey(), entry.getValue());
}
}
} else {
realUpdateValues.put(entry.getKey(), entry.getValue());
}
}
}
}
}
Object[] realValues = new Object[realUpdateValues.size() + valueSize];
if (valueSize > 0) {
System.arraycopy(values, 0, realValues, realUpdateValues.size(), valueSize);
}
String[] realNames = null;
if (isName) {
realNames = new String[realValues.length];
System.arraycopy(paramNames, 0, realNames, realUpdateValues.size(), valueSize);
}
// 强制修改的日期时间字段,且以数据库时间为准
IgnoreCaseSet forceUpdateSqlFields = new IgnoreCaseSet();
if (unifyHandler != null && SqlToyThreadDataHolder.useUnifyFields()
&& unifyHandler.forceUpdateFields() != null && unifyHandler.updateSqlTimeFields() != null) {
unifyHandler.forceUpdateFields().forEach((sqlUpdateField) -> {
if (unifyHandler.updateSqlTimeFields().contains(sqlUpdateField)) {
forceUpdateSqlFields.add(sqlUpdateField);
}
});
}
int index = 0;
String columnName;
Iterator> iter = realUpdateValues.entrySet().iterator();
String fieldSetValue;
// 设置一个扩展标志,避免set field=field+? 场景构造成field=field+:fieldExtParam跟where
// field=:field名称冲突
final String extSign = "SqlToyExtParam";
DataSource dsDataSource = getDataSource(innerModel.dataSource, null);
Integer dbType = DataSourceUtils.getDBType(sqlToyContext, dsDataSource);
String nvlFun = DataSourceUtils.getNvlFunction(dbType);
String currentTime;
String[] fields;
FieldMeta fieldMeta;
String fieldName;
Entry entry;
while (iter.hasNext()) {
entry = iter.next();
// 考虑 field=filed+? 模式,分割成2部分
fields = entry.getKey().split("=");
fieldMeta = entityMeta.getFieldMeta(fields[0].trim());
// entry.getKey() 直接是数据库字段名称
if (fieldMeta == null) {
// 先通过数据字段名称获得类的属性名称再获取fieldMeta
fieldName = entityMeta.getColumnFieldMap().get(fields[0].trim().toLowerCase());
fieldMeta = entityMeta.getFieldMeta(fieldName);
}
// 保留字处理
columnName = ReservedWordsUtil.convertWord(fieldMeta.getColumnName(), dbType);
if (isName) {
if (fields.length > 1) {
if (fields[1].contains("?")) {
// 拼接扩展字符,避免where后面有同样的参数名称
realNames[index] = fieldMeta.getFieldName().concat(extSign);
} else {
realNames[index] = SqlConfigParseUtils.getSqlParamsName(fields[1], true)[0];
}
} else {
// 2024-02-20拼接扩展字符,避免where后面有同样的参数名称
realNames[index] = fieldMeta.getFieldName().concat(extSign);
}
}
if (index > 0) {
sql.append(",");
}
currentTime = SqlUtil.getDBTime(dbType, fieldMeta, forceUpdateSqlFields);
// 将参数值设置为null,update语句构造成update table set field=nvl(?,current_timestamp) 形式
if (currentTime != null) {
// 原设定的参数设置为null,
realValues[index] = null;
// 剔除掉已经处理的字段
forceUpdateSqlFields.remove(fieldMeta.getFieldName());
// field=nvl(?,current_timestamp) 2024-02-20 add .concat(extSign)
sql.append(columnName).append("=").append(nvlFun).append("(")
.append(isName ? (":" + fieldMeta.getFieldName().concat(extSign)) : "?").append(",")
.append(currentTime).append(")");
} else {
realValues[index] = entry.getValue();
if (fields.length == 1) {
// 2024-02-20 add .concat(extSign)
sql.append(columnName).append("=")
.append(isName ? (":" + fieldMeta.getFieldName().concat(extSign)) : "?");
} else {
// field=filed+? 类似模式
fieldSetValue = fields[1];
sql.append(columnName).append("=");
if (isName && fieldSetValue.contains("?")) {
// 2024-02-20 add .concat(extSign)
fieldSetValue = fieldSetValue.replace("?", ":" + fieldMeta.getFieldName().concat(extSign));
}
fieldSetValue = SqlUtil.convertFieldsToColumns(entityMeta, fieldSetValue);
sql.append(fieldSetValue);
}
}
index++;
}
// 针对独立的sql 时间字段进行赋值更新,update table set field=current_timestamp
forceUpdateSqlFields.forEach((sqlField) -> {
FieldMeta sqlFieldMeta = entityMeta.getFieldMeta(sqlField);
String dateStr = SqlUtil.getDBTime(dbType, sqlFieldMeta, forceUpdateSqlFields);
if (dateStr != null) {
sql.append(",");
sql.append(ReservedWordsUtil.convertWord(sqlFieldMeta.getColumnName(), null)).append("=")
.append(dateStr);
}
});
// where条件是1=1,则跳过条件
if (!where.replaceAll("\\s", "").equals("1=1")) {
sql.append(" where ").append(where);
}
String sqlStr = sql.toString();
QueryExecutor queryExecutor = new QueryExecutor(sqlStr).names(realNames).values(realValues);
queryExecutor.getInnerModel().blankToNull = (innerModel.blankToNull == null)
? SqlToyConstants.executeSqlBlankToNull
: innerModel.blankToNull;
queryExecutor.getInnerModel().showSql = innerModel.showSql;
// 为后续租户过滤提供判断依据(单表简单sql和对应的实体对象)
queryExecutor.getInnerModel().entityClass = entityClass;
setEntitySharding(queryExecutor, entityMeta);
SqlToyConfig sqlToyConfig = sqlToyContext.getSqlToyConfig(queryExecutor, SqlType.update,
getDialect(dsDataSource));
sqlToyConfig.setSqlType(SqlType.update);
return dialectFactory.executeSql(sqlToyContext, sqlToyConfig, queryExecutor, null, null, dsDataSource);
}
/**
* @TODO 过滤掉无效set的属性
* @param updateValues
* @param entityMeta
* @param entityClass
* @param skipNotExistColumn
* @return
*/
private IgnoreCaseLinkedMap wrapRealUpdateValues(IgnoreCaseLinkedMap updateValues, EntityMeta entityMeta,
Class entityClass, boolean skipNotExistColumn) {
IgnoreCaseLinkedMap realUpdateValues = new IgnoreCaseLinkedMap();
// 先过滤不合法数据
Iterator> iter = updateValues.entrySet().iterator();
String[] fields;
FieldMeta fieldMeta;
String fieldName;
Entry entry;
String skipFields = new String();
while (iter.hasNext()) {
entry = iter.next();
fields = entry.getKey().split("=");
fieldMeta = entityMeta.getFieldMeta(fields[0].trim());
if (fieldMeta == null) {
// 先通过数据字段名称获得类的属性名称再获取fieldMeta
fieldName = entityMeta.getColumnFieldMap().get(fields[0].trim().toLowerCase());
if (fieldName != null) {
fieldMeta = entityMeta.getFieldMeta(fieldName);
}
}
if (fieldMeta == null) {
if (!skipNotExistColumn) {
throw new IllegalArgumentException("updateByQuery: 实体对象:" + entityClass.getName() + "属性:"
+ fields[0] + " 不是数据库表字段(@Column注解的表示数据库字段),请检查代码!");
} else {
if (skipFields.length() > 0) {
skipFields = skipFields.concat(",").concat(fields[0]);
} else {
skipFields = fields[0];
}
}
} else {
realUpdateValues.put(entry.getKey(), entry.getValue());
}
}
if (realUpdateValues.isEmpty()) {
throw new IllegalArgumentException("updateByQuery: 实体对象:" + entityClass.getName()
+ ",set(property,value)过程中,排除无效数据库字段属性:" + skipFields + "后,无有效更新属性(@Column注解的为数据库字段),请检查代码!");
}
return realUpdateValues;
}
/**
* @TODO 实现POJO和DTO(VO) 之间类型的相互转换和数据复制
* @param
* @param source
* @param resultType
* @param ignoreProperties
* @return
*/
protected T convertType(Serializable source, Class resultType,
String... ignoreProperties) {
return MapperUtils.map(source, resultType, ignoreProperties);
}
/**
* @TODO 实现POJO和DTO(VO) 集合之间类型的相互转换和数据复制
* @param
* @param sourceList
* @param resultType
* @param ignoreProperties
* @return
*/
protected List convertType(List sourceList, Class resultType,
String... ignoreProperties) {
return MapperUtils.mapList(sourceList, resultType, ignoreProperties);
}
protected Page convertType(Page sourcePage, Class resultType,
String... ignoreProperties) {
return MapperUtils.map(sourcePage, resultType, ignoreProperties);
}
// parallQuery 面向查询(不要用于事务操作过程中),sqltoy提供强大的方法,但是否恰当使用需要使用者做合理的判断
/**
* -- 避免开发者将全部功能用一个超级sql完成,提供拆解执行的同时确保执行效率,达到了效率和可维护的平衡
*
* @TODO 并行查询并返回一维List,有几个查询List中就包含几个结果对象,paramNames和paramValues是全部sql的条件参数的合集
* @param
* @param parallQueryList
* @param paramNames
* @param paramValues
* @return
*/
protected List> parallQuery(List parallQueryList, String[] paramNames,
Object[] paramValues) {
return parallQuery(parallQueryList, paramNames, paramValues, null);
}
protected List> parallQuery(List parallQueryList, Map paramsMap,
ParallelConfig parallelConfig) {
return parallQuery(parallQueryList, null, new Object[] { new IgnoreKeyCaseMap(paramsMap) }, parallelConfig);
}
/**
* @TODO 获取表的列信息
* @param catalog
* @param schema
* @param tableName
* @param dataSource
* @return
*/
protected List getTableColumns(final String catalog, final String schema, String tableName,
DataSource dataSource) {
return dialectFactory.getTableColumns(sqlToyContext, catalog, schema, tableName, getDataSource(dataSource));
}
/**
* @TODO 获取数据库的表信息
* @param catalog
* @param schema
* @param tableName
* @param dataSource
* @return
*/
protected List getTables(final String catalog, final String schema, String tableName,
DataSource dataSource) {
return dialectFactory.getTables(sqlToyContext, catalog, schema, tableName, getDataSource(dataSource));
}
/**
* @TODO 并行查询并返回一维List,有几个查询List中就包含几个结果对象,paramNames和paramValues是全部sql的条件参数的合集
* @param parallQueryList
* @param paramNames
* @param paramValues
* @param parallelConfig
* @return
*/
protected List> parallQuery(List parallQueryList, String[] paramNames,
Object[] paramValues, ParallelConfig parallelConfig) {
if (parallQueryList == null || parallQueryList.isEmpty()) {
return null;
}
ParallelConfig parallConfig = parallelConfig;
if (parallConfig == null) {
parallConfig = new ParallelConfig();
}
// 并行线程数量(默认最大十个)
if (parallConfig.getMaxThreads() == null) {
parallConfig.maxThreads(10);
}
int thread = parallConfig.getMaxThreads();
if (parallQueryList.size() < thread) {
thread = parallQueryList.size();
}
List> results = new ArrayList>();
ExecutorService pool = null;
try {
pool = Executors.newFixedThreadPool(thread);
List> futureResult = new ArrayList>();
SqlToyConfig sqlToyConfig;
Future future;
for (ParallQuery query : parallQueryList) {
sqlToyConfig = sqlToyContext.getSqlToyConfig(
new QueryExecutor(query.getExtend().sql).resultType(query.getExtend().resultType),
SqlType.search, getDialect(query.getExtend().dataSource));
// 自定义条件参数
if (query.getExtend().selfCondition) {
future = pool.submit(new ParallQueryExecutor(sqlToyContext, dialectFactory, sqlToyConfig, query,
query.getExtend().names, query.getExtend().values,
getDataSource(query.getExtend().dataSource, sqlToyConfig)));
} else {
future = pool.submit(new ParallQueryExecutor(sqlToyContext, dialectFactory, sqlToyConfig, query,
paramNames, paramValues, getDataSource(query.getExtend().dataSource, sqlToyConfig)));
}
futureResult.add(future);
}
pool.shutdown();
// 设置最大等待时长
if (parallConfig.getMaxWaitSeconds() != null) {
pool.awaitTermination(parallConfig.getMaxWaitSeconds(), TimeUnit.SECONDS);
} else {
pool.awaitTermination(SqlToyConstants.PARALLEL_MAXWAIT_SECONDS, TimeUnit.SECONDS);
}
ParallQueryResult item;
int index = 0;
for (Future result : futureResult) {
index++;
item = result.get();
// 存在执行异常则整体抛出
if (item != null && !item.isSuccess()) {
throw new DataAccessException("第:{} 个sql执行异常:{}!", index, item.getMessage());
}
results.add(item.getResult());
}
} catch (Exception e) {
e.printStackTrace();
throw new DataAccessException("并行查询执行错误:" + e.getMessage(), e);
} finally {
if (pool != null) {
pool.shutdownNow();
}
}
return results;
}
/**
* @TODO 获取当前数据库方言的名称
* @param dataSource
* @return
*/
protected String getDialect(DataSource dataSource) {
if (StringUtil.isNotBlank(sqlToyContext.getDialect())) {
return sqlToyContext.getDialect();
}
return DataSourceUtils.getDialect(sqlToyContext, getDataSource(dataSource));
}
private void setEntitySharding(QueryExecutor queryExecutor, EntityMeta entityMeta) {
// 分库分表策略
if (entityMeta.getShardingConfig() != null) {
// db sharding
if (entityMeta.getShardingConfig().getShardingDBStrategy() != null) {
queryExecutor.getInnerModel().dbSharding = entityMeta.getShardingConfig().getShardingDBStrategy();
}
// table sharding
if (entityMeta.getShardingConfig().getShardingTableStrategy() != null) {
List shardingConfig = new ArrayList();
shardingConfig.add(entityMeta.getShardingConfig().getShardingTableStrategy());
queryExecutor.getInnerModel().tableShardings = shardingConfig;
}
}
}
/**
* @TODO 验证实体类操作,对应实体对象是否合法
* @param entityMeta
* @param entityClass
* @param validatePK
*/
private void validEntity(EntityMeta entityMeta, Class entityClass, boolean validatePK) {
if (entityMeta == null) {
throw new IllegalArgumentException("Class=[" + entityClass.getName() + "]没有@Entity标记为POJO实体对象!");
}
if (entityMeta.getFieldsArray() == null || entityMeta.getFieldsArray().length == 0) {
throw new IllegalArgumentException("Class=[" + entityClass.getName() + "]没有@Column定义具体的字段信息!");
}
if (validatePK && (entityMeta.getIdArray() == null || entityMeta.getIdArray().length == 0)) {
throw new IllegalArgumentException("Class=[" + entityClass.getName() + "]没有@Id定义主键字段!");
}
}
}