From fe5ebc7ed732c6f061dfb4ea0bb0a45411e69fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=B8=85?= <1474983351@qq.com> Date: Tue, 13 Jun 2023 07:37:12 +0000 Subject: [PATCH] =?UTF-8?q?Revert=20"=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6?= =?UTF-8?q?=20mybatis-flex-core/src/main/java/com/mybatisflex/core/table/T?= =?UTF-8?q?ableInfo.java"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d496b18ab0fbcbd92cb5d1e942c19f6f85e14ce4. --- .../com/mybatisflex/core/table/TableInfo.java | 986 ++++++++++++++++++ 1 file changed, 986 insertions(+) create mode 100644 mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java new file mode 100644 index 00000000..f2985fc4 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java @@ -0,0 +1,986 @@ +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mybatisflex.core.table; + +import com.mybatisflex.annotation.InsertListener; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.SetListener; +import com.mybatisflex.annotation.UpdateListener; +import com.mybatisflex.core.FlexConsts; +import com.mybatisflex.core.FlexGlobalConfig; +import com.mybatisflex.core.constant.SqlConsts; +import com.mybatisflex.core.dialect.IDialect; +import com.mybatisflex.core.exception.FlexExceptions; +import com.mybatisflex.core.javassist.ModifyAttrsRecord; +import com.mybatisflex.core.mybatis.TypeHandlerObject; +import com.mybatisflex.core.query.*; +import com.mybatisflex.core.row.Row; +import com.mybatisflex.core.tenant.TenantManager; +import com.mybatisflex.core.util.*; +import org.apache.ibatis.mapping.ResultFlag; +import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.mapping.ResultMapping; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.Reflector; +import org.apache.ibatis.reflection.ReflectorFactory; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.type.TypeHandler; +import org.apache.ibatis.util.MapUtil; + +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public class TableInfo { + + private String schema; //schema + private String tableName; //表名 + private Class entityClass; //实体类 + private boolean camelToUnderline = true; + private String dataSource; + + //逻辑删除数据库列名 + private String logicDeleteColumn; + + //乐观锁字段 + private String versionColumn; + + //租户ID 字段 + private String tenantIdColumn; + + //数据插入时,默认插入数据字段 + private Map onInsertColumns; + + //数据更新时,默认更新内容的字段 + private Map onUpdateColumns; + + //大字段列 + private String[] largeColumns = new String[0]; + + // 所有的字段,但除了主键的列 + private String[] columns = new String[0]; + + //主键字段 + private String[] primaryKeys = new String[0]; + + // 默认查询列 + private String[] defaultColumns = new String[0]; + + //在插入数据的时候,支持主动插入的主键字段 + //通过自定义生成器生成 或者 Sequence 在 before 生成的时候,是需要主动插入数据的 + private String[] insertPrimaryKeys; + + private List columnInfoList; + private List primaryKeyList; + + //column 和 java 属性的称的关系映射 + private final Map columnInfoMapping = new HashMap<>(); + private final Map propertyColumnMapping = new HashMap<>(); + + private List onInsertListeners; + private List onUpdateListeners; + private List onSetListeners; + + /** + * @deprecated 该功能有更好的方式实现,此属性可能会被移除。 + */ + @Deprecated + private Map> joinTypes; + + + /** + * 对应 MapperXML 配置文件中 {@code } 标签下的 {@code } 标签。 + */ + private Map> associationType; + + /** + * 对应 MapperXML 配置文件中 {@code } 标签下的 {@code } 标签。 + */ + private Map> collectionType; + + + private final ReflectorFactory reflectorFactory = new BaseReflectorFactory() { + @Override + public Reflector findForClass(Class type) { + return getReflector(); + } + }; + private Reflector reflector; //反射工具 + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getTableName() { + return tableName; + } + + public String getWrapSchemaAndTableName(IDialect dialect) { + if (StringUtil.isNotBlank(schema)) { + return dialect.wrap(dialect.getRealSchema(schema)) + "." + dialect.wrap(dialect.getRealTable(tableName)); + } else { + return dialect.wrap(dialect.getRealTable(tableName)); + } + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public Class getEntityClass() { + return entityClass; + } + + public void setEntityClass(Class entityClass) { + this.entityClass = entityClass; + } + + public boolean isCamelToUnderline() { + return camelToUnderline; + } + + public void setCamelToUnderline(boolean camelToUnderline) { + this.camelToUnderline = camelToUnderline; + } + + public String getDataSource() { + return dataSource; + } + + public void setDataSource(String dataSource) { + this.dataSource = dataSource; + } + + public String getLogicDeleteColumn() { + return logicDeleteColumn; + } + + public void setLogicDeleteColumn(String logicDeleteColumn) { + this.logicDeleteColumn = logicDeleteColumn; + } + + public String getVersionColumn() { + return versionColumn; + } + + public void setVersionColumn(String versionColumn) { + this.versionColumn = versionColumn; + } + + public String getTenantIdColumn() { + return tenantIdColumn; + } + + public void setTenantIdColumn(String tenantIdColumn) { + this.tenantIdColumn = tenantIdColumn; + } + + public Map getOnInsertColumns() { + return onInsertColumns; + } + + public void setOnInsertColumns(Map onInsertColumns) { + this.onInsertColumns = onInsertColumns; + } + + public Map getOnUpdateColumns() { + return onUpdateColumns; + } + + public void setOnUpdateColumns(Map onUpdateColumns) { + this.onUpdateColumns = onUpdateColumns; + } + + public String[] getLargeColumns() { + return largeColumns; + } + + public void setLargeColumns(String[] largeColumns) { + this.largeColumns = largeColumns; + } + + public String[] getDefaultColumns() { + return defaultColumns; + } + + public void setDefaultColumns(String[] defaultColumns) { + this.defaultColumns = defaultColumns; + } + + public String[] getInsertPrimaryKeys() { + return insertPrimaryKeys; + } + + public void setInsertPrimaryKeys(String[] insertPrimaryKeys) { + this.insertPrimaryKeys = insertPrimaryKeys; + } + + public Reflector getReflector() { + return reflector; + } + + public ReflectorFactory getReflectorFactory() { + return reflectorFactory; + } + + public void setReflector(Reflector reflector) { + this.reflector = reflector; + } + + public String[] getColumns() { + return columns; + } + + + public void setColumns(String[] columns) { + this.columns = columns; + } + + public String[] getPrimaryKeys() { + return primaryKeys; + } + + public void setPrimaryKeys(String[] primaryKeys) { + this.primaryKeys = primaryKeys; + } + + + public List getOnInsertListeners() { + return onInsertListeners; + } + + public void setOnInsertListeners(List onInsertListeners) { + this.onInsertListeners = onInsertListeners; + } + + public List getOnUpdateListeners() { + return onUpdateListeners; + } + + public void setOnUpdateListeners(List onUpdateListeners) { + this.onUpdateListeners = onUpdateListeners; + } + + public List getOnSetListeners() { + return onSetListeners; + } + + public void setOnSetListeners(List onSetListeners) { + this.onSetListeners = onSetListeners; + } + + public List getColumnInfoList() { + return columnInfoList; + } + + public String getColumnByProperty(String property) { + return propertyColumnMapping.get(property); + } + + public Map> getJoinTypes() { + return joinTypes; + } + + public void setJoinTypes(Map> joinTypes) { + this.joinTypes = joinTypes; + } + + public void addJoinType(String fieldName, Class clazz) { + if (joinTypes == null) { + joinTypes = new HashMap<>(); + } + joinTypes.put(fieldName, clazz); + } + + public Map> getAssociationType() { + return associationType; + } + + public void setAssociationType(Map> associationType) { + this.associationType = associationType; + } + + public void addAssociationType(String fieldName, Class clazz) { + if (associationType == null) { + associationType = new HashMap<>(); + } + associationType.put(fieldName, clazz); + } + + public Map> getCollectionType() { + return collectionType; + } + + public void setCollectionType(Map> collectionType) { + this.collectionType = collectionType; + } + + public void addCollectionType(Field field, Class genericClass) { + if (collectionType == null) { + collectionType = new HashMap<>(); + } + collectionType.put(field, genericClass); + } + + void setColumnInfoList(List columnInfoList) { + this.columnInfoList = columnInfoList; + this.columns = new String[columnInfoList.size()]; + for (int i = 0; i < columnInfoList.size(); i++) { + ColumnInfo columnInfo = columnInfoList.get(i); + columns[i] = columnInfo.getColumn(); + columnInfoMapping.put(columnInfo.column, columnInfo); + propertyColumnMapping.put(columnInfo.property, columnInfo.column); + } + } + + + public List getPrimaryKeyList() { + return primaryKeyList; + } + + void setPrimaryKeyList(List primaryKeyList) { + this.primaryKeyList = primaryKeyList; + this.primaryKeys = new String[primaryKeyList.size()]; + + List insertIdFields = new ArrayList<>(); + for (int i = 0; i < primaryKeyList.size(); i++) { + IdInfo idInfo = primaryKeyList.get(i); + primaryKeys[i] = idInfo.getColumn(); + + if (idInfo.getKeyType() != KeyType.Auto && (idInfo.getBefore() != null && idInfo.getBefore())) { + insertIdFields.add(idInfo.getColumn()); + } + + columnInfoMapping.put(idInfo.column, idInfo); + propertyColumnMapping.put(idInfo.property, idInfo.column); + } + this.insertPrimaryKeys = insertIdFields.toArray(new String[0]); + } + + + /** + * 插入(新增)数据时,获取所有要插入的字段 + * + * @param entity + * @param ignoreNulls + * @return 字段列表 + */ + public String[] obtainInsertColumns(Object entity, boolean ignoreNulls) { + if (!ignoreNulls) { + return ArrayUtil.concat(insertPrimaryKeys, columns); + } else { + MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory); + List retColumns = new ArrayList<>(); + for (String insertColumn : columns) { + if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) { + retColumns.add(insertColumn); + } else { + Object value = buildColumnSqlArg(metaObject, insertColumn); + if (value == null) { + continue; + } + retColumns.add(insertColumn); + } + } + return ArrayUtil.concat(insertPrimaryKeys, retColumns.toArray(new String[0])); + } + } + + + /** + * 构建 insert 的 Sql 参数 + * + * @param entity 从 entity 中获取 + * @param ignoreNulls 是否忽略 null 值 + * @return 数组 + */ + public Object[] buildInsertSqlArgs(Object entity, boolean ignoreNulls) { + MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory); + String[] insertColumns = obtainInsertColumns(entity, ignoreNulls); + + List values = new ArrayList<>(insertColumns.length); + for (String insertColumn : insertColumns) { + if (onInsertColumns == null || !onInsertColumns.containsKey(insertColumn)) { + Object value = buildColumnSqlArg(metaObject, insertColumn); + if (ignoreNulls && value == null) { + continue; + } + values.add(value); + } + } + return values.toArray(); + } + + + /** + * 获取要修改的值 + * + * @param entity + * @param ignoreNulls + */ + public Set obtainUpdateColumns(Object entity, boolean ignoreNulls, boolean includePrimary) { + MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory); + Set columns = new LinkedHashSet<>(); //需使用 LinkedHashSet 保证 columns 的顺序 + if (entity instanceof ModifyAttrsRecord) { + Set properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs(); + if (properties.isEmpty()) { + return Collections.emptySet(); + } + for (String property : properties) { + String column = propertyColumnMapping.get(property); + if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) { + continue; + } + + //过滤乐观锁字段 和 租户字段 + if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) { + continue; + } + + if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) { + continue; + } + + // ModifyAttrsRecord 忽略 ignoreNulls 的设置 + // Object value = getPropertyValue(metaObject, property); + // if (ignoreNulls && value == null) { + // continue; + // } + columns.add(column); + } + } + //not ModifyAttrsRecord + else { + for (String column : this.columns) { + if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) { + continue; + } + + //过滤乐观锁字段 和 租户字段 + if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) { + continue; + } + + Object value = buildColumnSqlArg(metaObject, column); + if (ignoreNulls && value == null) { + continue; + } + + columns.add(column); + } + + // 普通 entity(非 ModifyAttrsRecord) 忽略 includePrimary 的设置 +// if (includePrimary) { +// for (String column : this.primaryKeys) { +// Object value = getColumnValue(metaObject, column); +// if (ignoreNulls && value == null) { +// continue; +// } +// columns.add(column); +// } +// } + } + return columns; + } + + /** + * 获取所有要修改的值,默认为全部除了主键以外的字段 + * + * @param entity 实体对象 + * @return 数组 + */ + public Object[] buildUpdateSqlArgs(Object entity, boolean ignoreNulls, boolean includePrimary) { + MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory); + List values = new ArrayList<>(); + if (entity instanceof ModifyAttrsRecord) { + Set properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs(); + if (properties.isEmpty()) { + return values.toArray(); + } + for (String property : properties) { + String column = propertyColumnMapping.get(property); + if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) { + continue; + } + //过滤乐观锁字段 和 租户字段 + if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) { + continue; + } + + if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) { + continue; + } + + Object value = buildColumnSqlArg(metaObject, column); + // ModifyAttrsRecord 忽略 ignoreNulls 的设置, + // 当使用 ModifyAttrsRecord 时,可以理解为要对字段进行 null 值进行更新,否则没必要使用 ModifyAttrsRecord + // if (ignoreNulls && value == null) { + // continue; + // } + values.add(value); + } + } + // normal entity. not ModifyAttrsRecord + else { + for (String column : this.columns) { + if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) { + continue; + } + + //过滤乐观锁字段 和 租户字段 + if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) { + continue; + } + + // 普通 entity 忽略 includePrimary 的设置, + // 因为 for 循环中的 this.columns 本身就不包含有主键 + // if (includePrimary) { + // } + + Object value = buildColumnSqlArg(metaObject, column); + if (ignoreNulls && value == null) { + continue; + } + + values.add(value); + } + } + + return values.toArray(); + } + + + /** + * 构建主键的 sql 参数数据 + * + * @param entity + */ + public Object[] buildPkSqlArgs(Object entity) { + MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory); + Object[] values = new Object[primaryKeys.length]; + for (int i = 0; i < primaryKeys.length; i++) { + values[i] = buildColumnSqlArg(metaObject, primaryKeys[i]); + } + return values; + } + + + public Object[] buildTenantIdArgs() { + if (StringUtil.isBlank(tenantIdColumn)) { + return null; + } + + return TenantManager.getTenantIds(); + } + + private static final String APPEND_CONDITIONS_FLAG = "appendConditions"; + + public void appendConditions(Object entity, QueryWrapper queryWrapper) { + + Object appendConditions = CPI.getContext(queryWrapper, APPEND_CONDITIONS_FLAG); + if (Boolean.TRUE.equals(appendConditions)) { + return; + } else { + CPI.putContext(queryWrapper, APPEND_CONDITIONS_FLAG, Boolean.TRUE); + } + + //select xxx.id,(select..) from xxx + List selectColumns = CPI.getSelectColumns(queryWrapper); + if (selectColumns != null && !selectColumns.isEmpty()) { + for (QueryColumn queryColumn : selectColumns) { + if (queryColumn instanceof SelectQueryColumn) { + QueryWrapper selectColumnQueryWrapper = CPI.getQueryWrapper((SelectQueryColumn) queryColumn); + doAppendConditions(entity, selectColumnQueryWrapper); + } + } + } + + //select * from (select ... from ) 中的子查询处理 + List queryTables = CPI.getQueryTables(queryWrapper); + if (queryTables != null && !queryTables.isEmpty()) { + for (QueryTable queryTable : queryTables) { + if (queryTable instanceof SelectQueryTable) { + QueryWrapper selectQueryWrapper = ((SelectQueryTable) queryTable).getQueryWrapper(); + doAppendConditions(entity, selectQueryWrapper); + } + } + } + + //添加乐观锁条件,只有在 update 的时候进行处理 + if (StringUtil.isNotBlank(versionColumn) && entity != null) { + Object versionValue = buildColumnSqlArg(entity, versionColumn); + if (versionValue == null) { + throw FlexExceptions.wrap("The version value of entity[%s] must not be null.", entity); + } + queryWrapper.and(QueryCondition.create(schema, tableName, versionColumn, SqlConsts.EQUALS, versionValue)); + } + + //逻辑删除 + if (StringUtil.isNotBlank(logicDeleteColumn)) { + queryWrapper.and(QueryCondition.create(schema, tableName, logicDeleteColumn, SqlConsts.EQUALS + , FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete())); + } + + //多租户 + Object[] tenantIdArgs = buildTenantIdArgs(); + if (ArrayUtil.isNotEmpty(tenantIdArgs)) { + if (tenantIdArgs.length == 1) { + queryWrapper.and(QueryCondition.create(schema, tableName, tenantIdColumn, SqlConsts.EQUALS, tenantIdArgs[0])); + } else { + queryWrapper.and(QueryCondition.create(schema, tableName, tenantIdColumn, SqlConsts.IN, tenantIdArgs)); + } + } + + //子查询 + List childSelects = CPI.getChildSelect(queryWrapper); + if (CollectionUtil.isNotEmpty(childSelects)) { + for (QueryWrapper childQueryWrapper : childSelects) { + doAppendConditions(entity, childQueryWrapper); + } + } + + //union + List unions = CPI.getUnions(queryWrapper); + if (CollectionUtil.isNotEmpty(unions)) { + for (UnionWrapper union : unions) { + QueryWrapper unionQueryWrapper = union.getQueryWrapper(); + doAppendConditions(entity, unionQueryWrapper); + } + } + } + + + private void doAppendConditions(Object entity, QueryWrapper queryWrapper) { + List queryTables = CPI.getQueryTables(queryWrapper); + if (queryTables != null && !queryTables.isEmpty()) { + for (QueryTable queryTable : queryTables) { + TableInfo tableInfo = TableInfoFactory.ofTableName(queryTable.getName()); + if (tableInfo != null) { + tableInfo.appendConditions(entity, queryWrapper); + } + } + } + } + + + public String getKeyProperties() { + StringJoiner joiner = new StringJoiner(","); + for (IdInfo value : primaryKeyList) { + joiner.add(FlexConsts.ENTITY + "." + value.getProperty()); + } + return joiner.toString(); + } + + + public String getKeyColumns() { + StringJoiner joiner = new StringJoiner(","); + for (IdInfo value : primaryKeyList) { + joiner.add(value.getColumn()); + } + return joiner.toString(); + } + + public List getDefaultQueryColumn() { + return Arrays.stream(defaultColumns) + .map(name -> new QueryColumn(schema, getTableName(), name)) + .collect(Collectors.toList()); + } + + + public ResultMap buildResultMap(Configuration configuration) { + String resultMapId = entityClass.getName(); + if (configuration.hasResultMap(resultMapId)) { + return configuration.getResultMap(resultMapId); + } + List resultMappings = new ArrayList<>(); + + // 标签下的 标签映射 + for (ColumnInfo columnInfo : columnInfoList) { + ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.property, + columnInfo.column, columnInfo.propertyType) + .jdbcType(columnInfo.getJdbcType()) + .typeHandler(columnInfo.buildTypeHandler()) + .build(); + resultMappings.add(mapping); + + //add property mapper for sql: select xxx as property ... + if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) { + ResultMapping propertyMapping = new ResultMapping.Builder(configuration, columnInfo.property, + columnInfo.property, columnInfo.propertyType) + .jdbcType(columnInfo.getJdbcType()) + .typeHandler(columnInfo.buildTypeHandler()) + .build(); + resultMappings.add(propertyMapping); + } + } + + // 标签下的 标签映射 + for (IdInfo idInfo : primaryKeyList) { + ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.property, + idInfo.column, idInfo.propertyType) + .flags(CollectionUtil.newArrayList(ResultFlag.ID)) + .jdbcType(idInfo.getJdbcType()) + .typeHandler(idInfo.buildTypeHandler()) + .build(); + resultMappings.add(mapping); + } + + // 标签下的 标签映射 + if (associationType != null) { + associationType.forEach((fieldName, fieldType) -> { + // 获取嵌套类型的信息,也就是 javaType 属性 + TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType); + // 构建嵌套类型的 ResultMap 对象,也就是 标签下的内容 + ResultMap nestedResultMap = tableInfo.buildResultMap(configuration); + resultMappings.add(new ResultMapping.Builder(configuration, fieldName) + .javaType(fieldType) + .nestedResultMapId(nestedResultMap.getId()) + .build()); + }); + } + + // 标签下的 标签映射 + if (collectionType != null) { + collectionType.forEach((field, genericClass) -> { + // 获取集合泛型类型的信息,也就是 ofType 属性 + TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass); + // 构建嵌套类型的 ResultMap 对象,也就是 标签下的内容 + ResultMap nestedResultMap = tableInfo.buildResultMap(configuration); + resultMappings.add(new ResultMapping.Builder(configuration, field.getName()) + .javaType(field.getType()) + .nestedResultMapId(nestedResultMap.getId()) + .build()); + }); + } + + ResultMap resultMap = new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build(); + configuration.addResultMap(resultMap); + return resultMap; + } + + + private Object buildColumnSqlArg(MetaObject metaObject, String column) { + ColumnInfo columnInfo = columnInfoMapping.get(column); + Object value = getPropertyValue(metaObject, columnInfo.property); + + if (value != null) { + TypeHandler typeHandler = columnInfo.buildTypeHandler(); + if (typeHandler != null) { + return new TypeHandlerObject(typeHandler, value, columnInfo.getJdbcType()); + } + } + + return value; + } + + + public Object buildColumnSqlArg(Object entityObject, String column) { + MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory); + return buildColumnSqlArg(metaObject, column); + } + + + private Object getPropertyValue(MetaObject metaObject, String property) { + if (property != null && metaObject.hasGetter(property)) { + return metaObject.getValue(property); + } + return null; + } + + + /** + * 通过 row 实例类转换为一个 entity + * + * @return entity + */ + public T newInstanceByRow(Row row, int index) { + Object instance = ClassUtil.newInstance(entityClass); + MetaObject metaObject = EntityMetaObject.forObject(instance, reflectorFactory); + Set rowKeys = row.keySet(); + columnInfoMapping.forEach((column, columnInfo) -> { + if (index <= 0) { + for (String rowKey : rowKeys) { + if (column.equalsIgnoreCase(rowKey)) { + setInstancePropertyValue(row, instance, metaObject, columnInfo, rowKey); + } + } + } else { + for (int i = index; i >= 0; i--) { + String newColumn = i <= 0 ? column : column + "$" + i; + boolean fillValue = false; + for (String rowKey : rowKeys) { + if (newColumn.equalsIgnoreCase(rowKey)) { + setInstancePropertyValue(row, instance, metaObject, columnInfo, rowKey); + fillValue = true; + break; + } + } + if (fillValue) { + break; + } + } + } + }); + return (T) instance; + } + + + private void setInstancePropertyValue(Row row, Object instance, MetaObject metaObject, ColumnInfo columnInfo, String rowKey) { + Object rowValue = row.get(rowKey); + TypeHandler typeHandler = columnInfo.buildTypeHandler(); + if (typeHandler != null) { + try { + //通过 typeHandler 转换数据 + rowValue = typeHandler.getResult(getResultSet(rowValue), 0); + } catch (SQLException e) { + //ignore + } + } + if (rowValue != null && !metaObject.getSetterType(columnInfo.property).isAssignableFrom(rowValue.getClass())) { + rowValue = ConvertUtil.convert(rowValue, metaObject.getSetterType(columnInfo.property), true); + } + rowValue = invokeOnSetListener(instance, columnInfo.getProperty(), rowValue); + metaObject.setValue(columnInfo.property, rowValue); + } + + + private ResultSet getResultSet(Object value) { + return (ResultSet) Proxy.newProxyInstance(TableInfo.class.getClassLoader(), + new Class[]{ResultSet.class}, (proxy, method, args) -> value); + } + + + /** + * 初始化乐观锁版本号 + * + * @param entityObject + */ + public void initVersionValueIfNecessary(Object entityObject) { + if (StringUtil.isBlank(versionColumn)) { + return; + } + + MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory); + Object columnValue = getPropertyValue(metaObject, columnInfoMapping.get(versionColumn).property); + if (columnValue == null) { + String name = columnInfoMapping.get(versionColumn).property; + Class clazz = metaObject.getSetterType(name); + metaObject.setValue(name, ConvertUtil.convert(0L, clazz)); + } + } + + /** + * 设置租户id + * + * @param entityObject + */ + public void initTenantIdIfNecessary(Object entityObject) { + if (StringUtil.isBlank(tenantIdColumn)) { + return; + } + + MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory); + Object[] tenantIds = TenantManager.getTenantIds(); + if (tenantIds == null || tenantIds.length == 0) { + return; + } + + //默认使用第一个作为插入的租户ID + Object tenantId = tenantIds[0]; + if (tenantId != null) { + String property = columnInfoMapping.get(tenantIdColumn).property; + Class setterType = metaObject.getSetterType(property); + metaObject.setValue(property, ConvertUtil.convert(tenantId, setterType)); + } + } + + /** + * 初始化逻辑删除的默认值 + * + * @param entityObject + */ + public void initLogicDeleteValueIfNecessary(Object entityObject) { + if (StringUtil.isBlank(logicDeleteColumn)) { + return; + } + + MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory); + Object columnValue = getPropertyValue(metaObject, columnInfoMapping.get(logicDeleteColumn).property); + if (columnValue == null) { + String property = columnInfoMapping.get(logicDeleteColumn).property; + Class setterType = metaObject.getSetterType(property); + Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete(); + metaObject.setValue(property, ConvertUtil.convert(normalValueOfLogicDelete, setterType)); + } + } + + + private static final Map, List> insertListenerCache = new ConcurrentHashMap<>(); + + public void invokeOnInsertListener(Object entity) { + List listeners = MapUtil.computeIfAbsent(insertListenerCache, entityClass, aClass -> { + List globalListeners = FlexGlobalConfig.getDefaultConfig() + .getSupportedInsertListener(entityClass, CollectionUtil.isNotEmpty(onInsertListeners)); + List allListeners = CollectionUtil.merge(onInsertListeners, globalListeners); + Collections.sort(allListeners); + return allListeners; + }); + listeners.forEach(insertListener -> insertListener.onInsert(entity)); + } + + + private static final Map, List> updateListenerCache = new ConcurrentHashMap<>(); + + public void invokeOnUpdateListener(Object entity) { + List listeners = MapUtil.computeIfAbsent(updateListenerCache, entityClass, aClass -> { + List globalListeners = FlexGlobalConfig.getDefaultConfig() + .getSupportedUpdateListener(entityClass, CollectionUtil.isNotEmpty(onUpdateListeners)); + List allListeners = CollectionUtil.merge(onUpdateListeners, globalListeners); + Collections.sort(allListeners); + return allListeners; + }); + listeners.forEach(insertListener -> insertListener.onUpdate(entity)); + } + + + private static final Map, List> setListenerCache = new ConcurrentHashMap<>(); + + public Object invokeOnSetListener(Object entity, String property, Object value) { + List listeners = MapUtil.computeIfAbsent(setListenerCache, entityClass, aClass -> { + List globalListeners = FlexGlobalConfig.getDefaultConfig() + .getSupportedSetListener(entityClass, CollectionUtil.isNotEmpty(onSetListeners)); + List allListeners = CollectionUtil.merge(onSetListeners, globalListeners); + Collections.sort(allListeners); + return allListeners; + }); + for (SetListener setListener : listeners) { + value = setListener.onSet(entity, property, value); + } + return value; + } + + public QueryColumn getQueryColumnByProperty(String property) { + return new QueryColumn(schema, tableName, propertyColumnMapping.get(property)); + } +}