001/*
002 *  Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.table;
017
018import com.mybatisflex.annotation.InsertListener;
019import com.mybatisflex.annotation.KeyType;
020import com.mybatisflex.annotation.SetListener;
021import com.mybatisflex.annotation.UpdateListener;
022import com.mybatisflex.core.FlexConsts;
023import com.mybatisflex.core.FlexGlobalConfig;
024import com.mybatisflex.core.dialect.IDialect;
025import com.mybatisflex.core.exception.FlexExceptions;
026import com.mybatisflex.core.javassist.ModifyAttrsRecord;
027import com.mybatisflex.core.mybatis.TypeHandlerObject;
028import com.mybatisflex.core.query.*;
029import com.mybatisflex.core.row.Row;
030import com.mybatisflex.core.tenant.TenantManager;
031import com.mybatisflex.core.util.*;
032import org.apache.ibatis.mapping.ResultFlag;
033import org.apache.ibatis.mapping.ResultMap;
034import org.apache.ibatis.mapping.ResultMapping;
035import org.apache.ibatis.reflection.MetaObject;
036import org.apache.ibatis.reflection.Reflector;
037import org.apache.ibatis.reflection.ReflectorFactory;
038import org.apache.ibatis.session.Configuration;
039import org.apache.ibatis.type.TypeHandler;
040import org.apache.ibatis.util.MapUtil;
041
042import java.lang.reflect.Field;
043import java.lang.reflect.Proxy;
044import java.sql.ResultSet;
045import java.sql.SQLException;
046import java.util.*;
047import java.util.concurrent.ConcurrentHashMap;
048import java.util.stream.Collectors;
049
050public class TableInfo {
051
052    private String schema; //schema
053    private String tableName; //表名
054    private Class<?> entityClass; //实体类
055    private boolean camelToUnderline = true;
056    private String dataSource;
057
058    //逻辑删除数据库列名
059    private String logicDeleteColumn;
060
061    //乐观锁字段
062    private String versionColumn;
063
064    //租户ID 字段
065    private String tenantIdColumn;
066
067    //数据插入时,默认插入数据字段
068    private Map<String, String> onInsertColumns;
069
070    //数据更新时,默认更新内容的字段
071    private Map<String, String> onUpdateColumns;
072
073    //大字段列
074    private String[] largeColumns = new String[0];
075
076    // 所有的字段,但除了主键的列
077    private String[] columns = new String[0];
078
079    //主键字段
080    private String[] primaryKeys = new String[0];
081
082    // 默认查询列
083    private String[] defaultColumns = new String[0];
084
085    //在插入数据的时候,支持主动插入的主键字段
086    //通过自定义生成器生成 或者 Sequence 在 before 生成的时候,是需要主动插入数据的
087    private String[] insertPrimaryKeys;
088
089    private List<ColumnInfo> columnInfoList;
090    private List<IdInfo> primaryKeyList;
091
092    //column 和 java 属性的称的关系映射
093    private final Map<String, ColumnInfo> columnInfoMapping = new HashMap<>();
094    private final Map<String, String> propertyColumnMapping = new HashMap<>();
095
096    private List<InsertListener> onInsertListeners;
097    private List<UpdateListener> onUpdateListeners;
098    private List<SetListener> onSetListeners;
099
100    /**
101     * @deprecated 该功能有更好的方式实现,此属性可能会被移除。
102     */
103    @Deprecated
104    private Map<String, Class<?>> joinTypes;
105
106
107    /**
108     * 对应 MapperXML 配置文件中 {@code <resultMap>} 标签下的 {@code <association>} 标签。
109     */
110    private Map<String, Class<?>> associationType;
111
112    /**
113     * 对应 MapperXML 配置文件中 {@code <resultMap>} 标签下的 {@code <collection>} 标签。
114     */
115    private Map<Field, Class<?>> collectionType;
116
117
118    private final ReflectorFactory reflectorFactory = new BaseReflectorFactory() {
119        @Override
120        public Reflector findForClass(Class<?> type) {
121            return getReflector();
122        }
123    };
124    private Reflector reflector; //反射工具
125
126    public String getSchema() {
127        return schema;
128    }
129
130    public void setSchema(String schema) {
131        this.schema = schema;
132    }
133
134    public String getTableName() {
135        return tableName;
136    }
137
138    public String getWrapSchemaAndTableName(IDialect dialect) {
139        if (StringUtil.isNotBlank(schema)) {
140            return dialect.wrap(dialect.getRealSchema(schema)) + "." + dialect.wrap(dialect.getRealTable(tableName));
141        } else {
142            return dialect.wrap(dialect.getRealTable(tableName));
143        }
144    }
145
146    public void setTableName(String tableName) {
147        this.tableName = tableName;
148    }
149
150    public Class<?> getEntityClass() {
151        return entityClass;
152    }
153
154    public void setEntityClass(Class<?> entityClass) {
155        this.entityClass = entityClass;
156    }
157
158    public boolean isCamelToUnderline() {
159        return camelToUnderline;
160    }
161
162    public void setCamelToUnderline(boolean camelToUnderline) {
163        this.camelToUnderline = camelToUnderline;
164    }
165
166    public String getDataSource() {
167        return dataSource;
168    }
169
170    public void setDataSource(String dataSource) {
171        this.dataSource = dataSource;
172    }
173
174    public String getLogicDeleteColumn() {
175        return logicDeleteColumn;
176    }
177
178    public void setLogicDeleteColumn(String logicDeleteColumn) {
179        this.logicDeleteColumn = logicDeleteColumn;
180    }
181
182    public String getVersionColumn() {
183        return versionColumn;
184    }
185
186    public void setVersionColumn(String versionColumn) {
187        this.versionColumn = versionColumn;
188    }
189
190    public String getTenantIdColumn() {
191        return tenantIdColumn;
192    }
193
194    public void setTenantIdColumn(String tenantIdColumn) {
195        this.tenantIdColumn = tenantIdColumn;
196    }
197
198    public Map<String, String> getOnInsertColumns() {
199        return onInsertColumns;
200    }
201
202    public void setOnInsertColumns(Map<String, String> onInsertColumns) {
203        this.onInsertColumns = onInsertColumns;
204    }
205
206    public Map<String, String> getOnUpdateColumns() {
207        return onUpdateColumns;
208    }
209
210    public void setOnUpdateColumns(Map<String, String> onUpdateColumns) {
211        this.onUpdateColumns = onUpdateColumns;
212    }
213
214    public String[] getLargeColumns() {
215        return largeColumns;
216    }
217
218    public void setLargeColumns(String[] largeColumns) {
219        this.largeColumns = largeColumns;
220    }
221
222    public String[] getDefaultColumns() {
223        return defaultColumns;
224    }
225
226    public void setDefaultColumns(String[] defaultColumns) {
227        this.defaultColumns = defaultColumns;
228    }
229
230    public String[] getInsertPrimaryKeys() {
231        return insertPrimaryKeys;
232    }
233
234    public void setInsertPrimaryKeys(String[] insertPrimaryKeys) {
235        this.insertPrimaryKeys = insertPrimaryKeys;
236    }
237
238    public Reflector getReflector() {
239        return reflector;
240    }
241
242    public ReflectorFactory getReflectorFactory() {
243        return reflectorFactory;
244    }
245
246    public void setReflector(Reflector reflector) {
247        this.reflector = reflector;
248    }
249
250    public String[] getColumns() {
251        return columns;
252    }
253
254
255    public void setColumns(String[] columns) {
256        this.columns = columns;
257    }
258
259    public String[] getPrimaryKeys() {
260        return primaryKeys;
261    }
262
263    public void setPrimaryKeys(String[] primaryKeys) {
264        this.primaryKeys = primaryKeys;
265    }
266
267
268    public List<InsertListener> getOnInsertListeners() {
269        return onInsertListeners;
270    }
271
272    public void setOnInsertListeners(List<InsertListener> onInsertListeners) {
273        this.onInsertListeners = onInsertListeners;
274    }
275
276    public List<UpdateListener> getOnUpdateListeners() {
277        return onUpdateListeners;
278    }
279
280    public void setOnUpdateListeners(List<UpdateListener> onUpdateListeners) {
281        this.onUpdateListeners = onUpdateListeners;
282    }
283
284    public List<SetListener> getOnSetListeners() {
285        return onSetListeners;
286    }
287
288    public void setOnSetListeners(List<SetListener> onSetListeners) {
289        this.onSetListeners = onSetListeners;
290    }
291
292    public List<ColumnInfo> getColumnInfoList() {
293        return columnInfoList;
294    }
295
296    public String getColumnByProperty(String property) {
297        return propertyColumnMapping.get(property);
298    }
299
300    public Map<String, Class<?>> getJoinTypes() {
301        return joinTypes;
302    }
303
304    public void setJoinTypes(Map<String, Class<?>> joinTypes) {
305        this.joinTypes = joinTypes;
306    }
307
308    public void addJoinType(String fieldName, Class<?> clazz) {
309        if (joinTypes == null) {
310            joinTypes = new HashMap<>();
311        }
312        joinTypes.put(fieldName, clazz);
313    }
314
315    public Map<String, Class<?>> getAssociationType() {
316        return associationType;
317    }
318
319    public void setAssociationType(Map<String, Class<?>> associationType) {
320        this.associationType = associationType;
321    }
322
323    public void addAssociationType(String fieldName, Class<?> clazz) {
324        if (associationType == null) {
325            associationType = new HashMap<>();
326        }
327        associationType.put(fieldName, clazz);
328    }
329
330    public Map<Field, Class<?>> getCollectionType() {
331        return collectionType;
332    }
333
334    public void setCollectionType(Map<Field, Class<?>> collectionType) {
335        this.collectionType = collectionType;
336    }
337
338    public void addCollectionType(Field field, Class<?> genericClass) {
339        if (collectionType == null) {
340            collectionType = new HashMap<>();
341        }
342        collectionType.put(field, genericClass);
343    }
344
345    void setColumnInfoList(List<ColumnInfo> columnInfoList) {
346        this.columnInfoList = columnInfoList;
347        this.columns = new String[columnInfoList.size()];
348        for (int i = 0; i < columnInfoList.size(); i++) {
349            ColumnInfo columnInfo = columnInfoList.get(i);
350            columns[i] = columnInfo.getColumn();
351            columnInfoMapping.put(columnInfo.column, columnInfo);
352            propertyColumnMapping.put(columnInfo.property, columnInfo.column);
353        }
354    }
355
356
357    public List<IdInfo> getPrimaryKeyList() {
358        return primaryKeyList;
359    }
360
361    void setPrimaryKeyList(List<IdInfo> primaryKeyList) {
362        this.primaryKeyList = primaryKeyList;
363        this.primaryKeys = new String[primaryKeyList.size()];
364
365        List<String> insertIdFields = new ArrayList<>();
366        for (int i = 0; i < primaryKeyList.size(); i++) {
367            IdInfo idInfo = primaryKeyList.get(i);
368            primaryKeys[i] = idInfo.getColumn();
369
370            if (idInfo.getKeyType() != KeyType.Auto && (idInfo.getBefore() != null && idInfo.getBefore())) {
371                insertIdFields.add(idInfo.getColumn());
372            }
373
374            columnInfoMapping.put(idInfo.column, idInfo);
375            propertyColumnMapping.put(idInfo.property, idInfo.column);
376        }
377        this.insertPrimaryKeys = insertIdFields.toArray(new String[0]);
378    }
379
380
381    /**
382     * 插入(新增)数据时,获取所有要插入的字段
383     *
384     * @param entity
385     * @param ignoreNulls
386     * @return 字段列表
387     */
388    public String[] obtainInsertColumns(Object entity, boolean ignoreNulls) {
389        if (!ignoreNulls) {
390            return ArrayUtil.concat(insertPrimaryKeys, columns);
391        } else {
392            MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
393            List<String> retColumns = new ArrayList<>();
394            for (String insertColumn : columns) {
395                if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
396                    retColumns.add(insertColumn);
397                } else {
398                    Object value = buildColumnSqlArg(metaObject, insertColumn);
399                    if (value == null) {
400                        continue;
401                    }
402                    retColumns.add(insertColumn);
403                }
404            }
405            return ArrayUtil.concat(insertPrimaryKeys, retColumns.toArray(new String[0]));
406        }
407    }
408
409
410    /**
411     * 构建 insert 的 Sql 参数
412     *
413     * @param entity      从 entity 中获取
414     * @param ignoreNulls 是否忽略 null 值
415     * @return 数组
416     */
417    public Object[] buildInsertSqlArgs(Object entity, boolean ignoreNulls) {
418        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
419        String[] insertColumns = obtainInsertColumns(entity, ignoreNulls);
420
421        List<Object> values = new ArrayList<>(insertColumns.length);
422        for (String insertColumn : insertColumns) {
423            if (onInsertColumns == null || !onInsertColumns.containsKey(insertColumn)) {
424                Object value = buildColumnSqlArg(metaObject, insertColumn);
425                if (ignoreNulls && value == null) {
426                    continue;
427                }
428                values.add(value);
429            }
430        }
431        return values.toArray();
432    }
433
434
435    /**
436     * 获取要修改的值
437     *
438     * @param entity
439     * @param ignoreNulls
440     */
441    public Set<String> obtainUpdateColumns(Object entity, boolean ignoreNulls, boolean includePrimary) {
442        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
443        Set<String> columns = new LinkedHashSet<>(); //需使用 LinkedHashSet 保证 columns 的顺序
444        if (entity instanceof ModifyAttrsRecord) {
445            Set<String> properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs();
446            if (properties.isEmpty()) {
447                return Collections.emptySet();
448            }
449            for (String property : properties) {
450                String column = propertyColumnMapping.get(property);
451                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
452                    continue;
453                }
454
455                //过滤乐观锁字段 和 租户字段
456                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
457                    continue;
458                }
459
460                if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) {
461                    continue;
462                }
463
464                // ModifyAttrsRecord 忽略 ignoreNulls 的设置
465                // Object value = getPropertyValue(metaObject, property);
466                // if (ignoreNulls && value == null) {
467                //     continue;
468                // }
469                columns.add(column);
470            }
471        }
472        //not ModifyAttrsRecord
473        else {
474            for (String column : this.columns) {
475                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
476                    continue;
477                }
478
479                //过滤乐观锁字段 和 租户字段
480                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
481                    continue;
482                }
483
484                Object value = buildColumnSqlArg(metaObject, column);
485                if (ignoreNulls && value == null) {
486                    continue;
487                }
488
489                columns.add(column);
490            }
491
492            // 普通 entity(非 ModifyAttrsRecord) 忽略 includePrimary 的设置
493//            if (includePrimary) {
494//                for (String column : this.primaryKeys) {
495//                    Object value = getColumnValue(metaObject, column);
496//                    if (ignoreNulls && value == null) {
497//                        continue;
498//                    }
499//                    columns.add(column);
500//                }
501//            }
502        }
503        return columns;
504    }
505
506    /**
507     * 获取所有要修改的值,默认为全部除了主键以外的字段
508     *
509     * @param entity 实体对象
510     * @return 数组
511     */
512    public Object[] buildUpdateSqlArgs(Object entity, boolean ignoreNulls, boolean includePrimary) {
513        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
514        List<Object> values = new ArrayList<>();
515        if (entity instanceof ModifyAttrsRecord) {
516            Set<String> properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs();
517            if (properties.isEmpty()) {
518                return values.toArray();
519            }
520            for (String property : properties) {
521                String column = propertyColumnMapping.get(property);
522                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
523                    continue;
524                }
525                //过滤乐观锁字段 和 租户字段
526                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
527                    continue;
528                }
529
530                if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) {
531                    continue;
532                }
533
534                Object value = buildColumnSqlArg(metaObject, column);
535                // ModifyAttrsRecord 忽略 ignoreNulls 的设置,
536                // 当使用 ModifyAttrsRecord 时,可以理解为要对字段进行 null 值进行更新,否则没必要使用 ModifyAttrsRecord
537                // if (ignoreNulls && value == null) {
538                //    continue;
539                // }
540                values.add(value);
541            }
542        }
543        // normal entity. not ModifyAttrsRecord
544        else {
545            for (String column : this.columns) {
546                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
547                    continue;
548                }
549
550                //过滤乐观锁字段 和 租户字段
551                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
552                    continue;
553                }
554
555                // 普通 entity 忽略 includePrimary 的设置,
556                // 因为 for 循环中的 this.columns 本身就不包含有主键
557                // if (includePrimary) {
558                // }
559
560                Object value = buildColumnSqlArg(metaObject, column);
561                if (ignoreNulls && value == null) {
562                    continue;
563                }
564
565                values.add(value);
566            }
567        }
568
569        return values.toArray();
570    }
571
572
573    /**
574     * 构建主键的 sql 参数数据
575     *
576     * @param entity
577     */
578    public Object[] buildPkSqlArgs(Object entity) {
579        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
580        Object[] values = new Object[primaryKeys.length];
581        for (int i = 0; i < primaryKeys.length; i++) {
582            values[i] = buildColumnSqlArg(metaObject, primaryKeys[i]);
583        }
584        return values;
585    }
586
587
588    public Object[] buildTenantIdArgs() {
589        if (StringUtil.isBlank(tenantIdColumn)) {
590            return null;
591        }
592
593        return TenantManager.getTenantIds();
594    }
595
596    private static final String APPEND_CONDITIONS_FLAG = "appendConditions";
597
598    public void appendConditions(Object entity, QueryWrapper queryWrapper) {
599
600        Object appendConditions = CPI.getContext(queryWrapper, APPEND_CONDITIONS_FLAG);
601        if (Boolean.TRUE.equals(appendConditions)) {
602            return;
603        } else {
604            CPI.putContext(queryWrapper, APPEND_CONDITIONS_FLAG, Boolean.TRUE);
605        }
606
607        //select xxx.id,(select..) from xxx
608        List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
609        if (selectColumns != null && !selectColumns.isEmpty()) {
610            for (QueryColumn queryColumn : selectColumns) {
611                if (queryColumn instanceof SelectQueryColumn) {
612                    QueryWrapper selectColumnQueryWrapper = CPI.getQueryWrapper((SelectQueryColumn) queryColumn);
613                    doAppendConditions(entity, selectColumnQueryWrapper);
614                }
615            }
616        }
617
618        //select * from (select ... from ) 中的子查询处理
619        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
620        if (queryTables != null && !queryTables.isEmpty()) {
621            for (QueryTable queryTable : queryTables) {
622                if (queryTable instanceof SelectQueryTable) {
623                    QueryWrapper selectQueryWrapper = ((SelectQueryTable) queryTable).getQueryWrapper();
624                    doAppendConditions(entity, selectQueryWrapper);
625                }
626            }
627        }
628
629        //添加乐观锁条件,只有在 update 的时候进行处理
630        if (StringUtil.isNotBlank(versionColumn) && entity != null) {
631            Object versionValue = buildColumnSqlArg(entity, versionColumn);
632            if (versionValue == null) {
633                throw FlexExceptions.wrap("The version value of entity[%s] must not be null.", entity);
634            }
635            queryWrapper.and(QueryCondition.create(schema, tableName, versionColumn, QueryCondition.LOGIC_EQUALS, versionValue));
636        }
637
638        //逻辑删除
639        if (StringUtil.isNotBlank(logicDeleteColumn)) {
640            queryWrapper.and(QueryCondition.create(schema, tableName, logicDeleteColumn, QueryCondition.LOGIC_EQUALS
641                    , FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete()));
642        }
643
644        //多租户
645        Object[] tenantIdArgs = buildTenantIdArgs();
646        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
647            if (tenantIdArgs.length == 1) {
648                queryWrapper.and(QueryCondition.create(schema, tableName, tenantIdColumn, QueryCondition.LOGIC_EQUALS, tenantIdArgs[0]));
649            } else {
650                queryWrapper.and(QueryCondition.create(schema, tableName, tenantIdColumn, QueryCondition.LOGIC_IN, tenantIdArgs));
651            }
652        }
653
654        //子查询
655        List<QueryWrapper> childSelects = CPI.getChildSelect(queryWrapper);
656        if (CollectionUtil.isNotEmpty(childSelects)) {
657            for (QueryWrapper childQueryWrapper : childSelects) {
658                doAppendConditions(entity, childQueryWrapper);
659            }
660        }
661
662        //union
663        List<UnionWrapper> unions = CPI.getUnions(queryWrapper);
664        if (CollectionUtil.isNotEmpty(unions)) {
665            for (UnionWrapper union : unions) {
666                QueryWrapper unionQueryWrapper = union.getQueryWrapper();
667                doAppendConditions(entity, unionQueryWrapper);
668            }
669        }
670    }
671
672
673    private void doAppendConditions(Object entity, QueryWrapper queryWrapper) {
674        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
675        if (queryTables != null && !queryTables.isEmpty()) {
676            for (QueryTable queryTable : queryTables) {
677                TableInfo tableInfo = TableInfoFactory.ofTableName(queryTable.getName());
678                if (tableInfo != null) {
679                    tableInfo.appendConditions(entity, queryWrapper);
680                }
681            }
682        }
683    }
684
685
686    public String getKeyProperties() {
687        StringJoiner joiner = new StringJoiner(",");
688        for (IdInfo value : primaryKeyList) {
689            joiner.add(FlexConsts.ENTITY + "." + value.getProperty());
690        }
691        return joiner.toString();
692    }
693
694
695    public String getKeyColumns() {
696        StringJoiner joiner = new StringJoiner(",");
697        for (IdInfo value : primaryKeyList) {
698            joiner.add(value.getColumn());
699        }
700        return joiner.toString();
701    }
702
703    public List<QueryColumn> getDefaultQueryColumn() {
704        return Arrays.stream(defaultColumns)
705                .map(name -> new QueryColumn(schema, getTableName(), name))
706                .collect(Collectors.toList());
707    }
708
709    /**
710     * @deprecated 该功能有更好的方式实现,此方法可能会被移除。
711     */
712    @Deprecated
713    public ResultMap buildResultMap(Configuration configuration) {
714        String resultMapId = entityClass.getName();
715        List<ResultMapping> resultMappings = new ArrayList<>();
716
717        for (ColumnInfo columnInfo : columnInfoList) {
718            ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.property,
719                    columnInfo.column, columnInfo.propertyType)
720                    .jdbcType(columnInfo.getJdbcType())
721                    .typeHandler(columnInfo.buildTypeHandler())
722                    .build();
723            resultMappings.add(mapping);
724
725            //add property mapper for sql: select xxx as property ...
726            if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) {
727                ResultMapping propertyMapping = new ResultMapping.Builder(configuration, columnInfo.property,
728                        columnInfo.property, columnInfo.propertyType)
729                        .jdbcType(columnInfo.getJdbcType())
730                        .typeHandler(columnInfo.buildTypeHandler())
731                        .build();
732                resultMappings.add(propertyMapping);
733            }
734        }
735
736        for (IdInfo idInfo : primaryKeyList) {
737            ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.property,
738                    idInfo.column, idInfo.propertyType)
739                    .flags(CollectionUtil.newArrayList(ResultFlag.ID))
740                    .jdbcType(idInfo.getJdbcType())
741                    .typeHandler(idInfo.buildTypeHandler())
742                    .build();
743            resultMappings.add(mapping);
744        }
745
746        if (joinTypes != null && !joinTypes.isEmpty()) {
747            joinTypes.forEach((fieldName, fieldType) -> {
748
749                TableInfo joinTableInfo = TableInfoFactory.ofEntityClass(fieldType);
750                List<ColumnInfo> joinTableInfoColumnInfoList = joinTableInfo.getColumnInfoList();
751
752                for (ColumnInfo joinColumnInfo : joinTableInfoColumnInfoList) {
753                    if (!existColumn(resultMappings, joinColumnInfo.column)) {
754                        ResultMapping mapping = new ResultMapping.Builder(configuration, fieldName + "." + joinColumnInfo.property,
755                                joinColumnInfo.column, joinColumnInfo.propertyType)
756                                .jdbcType(joinColumnInfo.jdbcType)
757                                .typeHandler(joinColumnInfo.buildTypeHandler())
758                                .build();
759                        resultMappings.add(mapping);
760                    }
761
762                    //add property mapper for sql: select xxx as property ...
763                    if (!existColumn(resultMappings, joinColumnInfo.property)) {
764                        if (!Objects.equals(joinColumnInfo.column, joinColumnInfo.property)) {
765                            ResultMapping propertyMapping = new ResultMapping.Builder(configuration, fieldName + "." + joinColumnInfo.property,
766                                    joinColumnInfo.property, joinColumnInfo.propertyType)
767                                    .jdbcType(joinColumnInfo.jdbcType)
768                                    .typeHandler(joinColumnInfo.buildTypeHandler())
769                                    .build();
770                            resultMappings.add(propertyMapping);
771                        }
772                    }
773                }
774            });
775        }
776
777        return new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
778    }
779
780    public List<ResultMap> buildResultMapList(Configuration configuration) {
781        String resultMapId = entityClass.getName();
782        List<ResultMap> resultMaps = new ArrayList<>();
783        List<ResultMapping> resultMappings = new ArrayList<>();
784
785        // <resultMap> 标签下的 <result> 标签映射
786        for (ColumnInfo columnInfo : columnInfoList) {
787            ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.property,
788                    columnInfo.column, columnInfo.propertyType)
789                    .jdbcType(columnInfo.getJdbcType())
790                    .typeHandler(columnInfo.buildTypeHandler())
791                    .build();
792            resultMappings.add(mapping);
793
794            //add property mapper for sql: select xxx as property ...
795            if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) {
796                ResultMapping propertyMapping = new ResultMapping.Builder(configuration, columnInfo.property,
797                        columnInfo.property, columnInfo.propertyType)
798                        .jdbcType(columnInfo.getJdbcType())
799                        .typeHandler(columnInfo.buildTypeHandler())
800                        .build();
801                resultMappings.add(propertyMapping);
802            }
803        }
804
805        // <resultMap> 标签下的 <id> 标签映射
806        for (IdInfo idInfo : primaryKeyList) {
807            ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.property,
808                    idInfo.column, idInfo.propertyType)
809                    .flags(CollectionUtil.newArrayList(ResultFlag.ID))
810                    .jdbcType(idInfo.getJdbcType())
811                    .typeHandler(idInfo.buildTypeHandler())
812                    .build();
813            resultMappings.add(mapping);
814        }
815
816        // <resultMap> 标签下的 <association> 标签映射
817        if (associationType != null) {
818            associationType.forEach((fieldName, fieldType) -> {
819                // 获取嵌套类型的信息,也就是 javaType 属性
820                TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType);
821                // 构建嵌套类型的 ResultMap 对象,也就是 <association> 标签下的内容
822                // 这里是递归调用,直到嵌套类型里面没有其他嵌套类型或者集合类型为止
823                List<ResultMap> resultMapList = tableInfo.buildResultMapList(configuration);
824                // 寻找是否有嵌套 ResultMap 引用
825                Optional<ResultMap> nestedResultMap = resultMapList.stream()
826                        .filter(e -> fieldType.getName().equals(e.getId()))
827                        .findFirst();
828                // 处理嵌套类型 ResultMapping 引用
829                nestedResultMap.ifPresent(resultMap -> resultMappings.add(new ResultMapping.Builder(configuration, fieldName)
830                        .javaType(fieldType)
831                        .nestedResultMapId(resultMap.getId())
832                        .build()));
833                // 全部添加到 ResultMap 集合当中
834                resultMaps.addAll(resultMapList);
835            });
836        }
837
838        // <resultMap> 标签下的 <collection> 标签映射
839        if (collectionType != null) {
840            collectionType.forEach((field, genericClass) -> {
841                // 获取集合泛型类型的信息,也就是 ofType 属性
842                TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass);
843                // 构建嵌套类型的 ResultMap 对象,也就是 <collection> 标签下的内容
844                // 这里是递归调用,直到集合类型里面没有其他嵌套类型或者集合类型为止
845                List<ResultMap> resultMapList = tableInfo.buildResultMapList(configuration);
846                // 寻找是否有嵌套 ResultMap 引用
847                Optional<ResultMap> nestedResultMap = resultMapList.stream()
848                        .filter(e -> genericClass.getName().equals(e.getId()))
849                        .findFirst();
850                // 处理嵌套类型 ResultMapping 引用
851                nestedResultMap.ifPresent(resultMap -> resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
852                        .javaType(field.getType())
853                        .nestedResultMapId(resultMap.getId())
854                        .build()));
855                // 全部添加到 ResultMap 集合当中
856                resultMaps.addAll(resultMapList);
857            });
858        }
859
860        resultMaps.add(new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build());
861
862        return resultMaps;
863    }
864
865    private static boolean existColumn(List<ResultMapping> resultMappings, String name) {
866        for (ResultMapping resultMapping : resultMappings) {
867            if (resultMapping.getColumn().equalsIgnoreCase(name)) {
868                return true;
869            }
870        }
871        return false;
872    }
873
874
875    private Object buildColumnSqlArg(MetaObject metaObject, String column) {
876        ColumnInfo columnInfo = columnInfoMapping.get(column);
877        Object value = getPropertyValue(metaObject, columnInfo.property);
878
879        if (value != null) {
880            TypeHandler typeHandler = columnInfo.buildTypeHandler();
881            if (typeHandler != null) {
882                return new TypeHandlerObject(typeHandler, value, columnInfo.getJdbcType());
883            }
884        }
885
886        return value;
887    }
888
889
890    public Object buildColumnSqlArg(Object entityObject, String column) {
891        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
892        return buildColumnSqlArg(metaObject, column);
893    }
894
895
896    private Object getPropertyValue(MetaObject metaObject, String property) {
897        if (property != null && metaObject.hasGetter(property)) {
898            return metaObject.getValue(property);
899        }
900        return null;
901    }
902
903
904    /**
905     * 通过 row 实例类转换为一个 entity
906     *
907     * @return entity
908     */
909    public <T> T newInstanceByRow(Row row, int index) {
910        Object instance = ClassUtil.newInstance(entityClass);
911        MetaObject metaObject = EntityMetaObject.forObject(instance, reflectorFactory);
912        Set<String> rowKeys = row.keySet();
913        columnInfoMapping.forEach((column, columnInfo) -> {
914            if (index <= 0) {
915                for (String rowKey : rowKeys) {
916                    if (column.equalsIgnoreCase(rowKey)) {
917                        setInstancePropertyValue(row, instance, metaObject, columnInfo, rowKey);
918                    }
919                }
920            } else {
921                for (int i = index; i >= 0; i--) {
922                    String newColumn = i <= 0 ? column : column + "$" + i;
923                    boolean fillValue = false;
924                    for (String rowKey : rowKeys) {
925                        if (newColumn.equalsIgnoreCase(rowKey)) {
926                            setInstancePropertyValue(row, instance, metaObject, columnInfo, rowKey);
927                            fillValue = true;
928                            break;
929                        }
930                    }
931                    if (fillValue) {
932                        break;
933                    }
934                }
935            }
936        });
937        return (T) instance;
938    }
939
940
941    private void setInstancePropertyValue(Row row, Object instance, MetaObject metaObject, ColumnInfo columnInfo, String rowKey) {
942        Object rowValue = row.get(rowKey);
943        TypeHandler<?> typeHandler = columnInfo.buildTypeHandler();
944        if (typeHandler != null) {
945            try {
946                //通过 typeHandler 转换数据
947                rowValue = typeHandler.getResult(getResultSet(rowValue), 0);
948            } catch (SQLException e) {
949                //ignore
950            }
951        }
952        if (rowValue != null && !metaObject.getSetterType(columnInfo.property).isAssignableFrom(rowValue.getClass())) {
953            rowValue = ConvertUtil.convert(rowValue, metaObject.getSetterType(columnInfo.property), true);
954        }
955        rowValue = invokeOnSetListener(instance, columnInfo.getProperty(), rowValue);
956        metaObject.setValue(columnInfo.property, rowValue);
957    }
958
959
960    private ResultSet getResultSet(Object value) {
961        return (ResultSet) Proxy.newProxyInstance(TableInfo.class.getClassLoader(),
962                new Class[]{ResultSet.class}, (proxy, method, args) -> value);
963    }
964
965
966    /**
967     * 初始化乐观锁版本号
968     *
969     * @param entityObject
970     */
971    public void initVersionValueIfNecessary(Object entityObject) {
972        if (StringUtil.isBlank(versionColumn)) {
973            return;
974        }
975
976        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
977        Object columnValue = getPropertyValue(metaObject, columnInfoMapping.get(versionColumn).property);
978        if (columnValue == null) {
979            String name = columnInfoMapping.get(versionColumn).property;
980            Class<?> clazz = metaObject.getSetterType(name);
981            metaObject.setValue(name, ConvertUtil.convert(0L, clazz));
982        }
983    }
984
985    /**
986     * 设置租户id
987     *
988     * @param entityObject
989     */
990    public void initTenantIdIfNecessary(Object entityObject) {
991        if (StringUtil.isBlank(tenantIdColumn)) {
992            return;
993        }
994
995        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
996        Object[] tenantIds = TenantManager.getTenantIds();
997        if (tenantIds == null || tenantIds.length == 0) {
998            return;
999        }
1000
1001        //默认使用第一个作为插入的租户ID
1002        Object tenantId = tenantIds[0];
1003        if (tenantId != null) {
1004            String property = columnInfoMapping.get(tenantIdColumn).property;
1005            Class<?> setterType = metaObject.getSetterType(property);
1006            metaObject.setValue(property, ConvertUtil.convert(tenantId, setterType));
1007        }
1008    }
1009
1010    /**
1011     * 初始化逻辑删除的默认值
1012     *
1013     * @param entityObject
1014     */
1015    public void initLogicDeleteValueIfNecessary(Object entityObject) {
1016        if (StringUtil.isBlank(logicDeleteColumn)) {
1017            return;
1018        }
1019
1020        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
1021        Object columnValue = getPropertyValue(metaObject, columnInfoMapping.get(logicDeleteColumn).property);
1022        if (columnValue == null) {
1023            String property = columnInfoMapping.get(logicDeleteColumn).property;
1024            Class<?> setterType = metaObject.getSetterType(property);
1025            Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
1026            metaObject.setValue(property, ConvertUtil.convert(normalValueOfLogicDelete, setterType));
1027        }
1028    }
1029
1030
1031    private static final Map<Class<?>, List<InsertListener>> insertListenerCache = new ConcurrentHashMap<>();
1032
1033    public void invokeOnInsertListener(Object entity) {
1034        List<InsertListener> listeners = MapUtil.computeIfAbsent(insertListenerCache, entityClass, aClass -> {
1035            List<InsertListener> globalListeners = FlexGlobalConfig.getDefaultConfig()
1036                    .getSupportedInsertListener(entityClass, CollectionUtil.isNotEmpty(onInsertListeners));
1037            List<InsertListener> allListeners = CollectionUtil.merge(onInsertListeners, globalListeners);
1038            Collections.sort(allListeners);
1039            return allListeners;
1040        });
1041        listeners.forEach(insertListener -> insertListener.onInsert(entity));
1042    }
1043
1044
1045    private static final Map<Class<?>, List<UpdateListener>> updateListenerCache = new ConcurrentHashMap<>();
1046
1047    public void invokeOnUpdateListener(Object entity) {
1048        List<UpdateListener> listeners = MapUtil.computeIfAbsent(updateListenerCache, entityClass, aClass -> {
1049            List<UpdateListener> globalListeners = FlexGlobalConfig.getDefaultConfig()
1050                    .getSupportedUpdateListener(entityClass, CollectionUtil.isNotEmpty(onUpdateListeners));
1051            List<UpdateListener> allListeners = CollectionUtil.merge(onUpdateListeners, globalListeners);
1052            Collections.sort(allListeners);
1053            return allListeners;
1054        });
1055        listeners.forEach(insertListener -> insertListener.onUpdate(entity));
1056    }
1057
1058
1059    private static final Map<Class<?>, List<SetListener>> setListenerCache = new ConcurrentHashMap<>();
1060
1061    public Object invokeOnSetListener(Object entity, String property, Object value) {
1062        List<SetListener> listeners = MapUtil.computeIfAbsent(setListenerCache, entityClass, aClass -> {
1063            List<SetListener> globalListeners = FlexGlobalConfig.getDefaultConfig()
1064                    .getSupportedSetListener(entityClass, CollectionUtil.isNotEmpty(onSetListeners));
1065            List<SetListener> allListeners = CollectionUtil.merge(onSetListeners, globalListeners);
1066            Collections.sort(allListeners);
1067            return allListeners;
1068        });
1069        for (SetListener setListener : listeners) {
1070            value = setListener.onSet(entity, property, value);
1071        }
1072        return value;
1073    }
1074
1075    public QueryColumn getQueryColumnByProperty(String property) {
1076        return new QueryColumn(schema, tableName, propertyColumnMapping.get(property));
1077    }
1078}