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.Column;
019import com.mybatisflex.annotation.InsertListener;
020import com.mybatisflex.annotation.KeyType;
021import com.mybatisflex.annotation.SetListener;
022import com.mybatisflex.annotation.UpdateListener;
023import com.mybatisflex.core.FlexConsts;
024import com.mybatisflex.core.FlexGlobalConfig;
025import com.mybatisflex.core.constant.SqlConsts;
026import com.mybatisflex.core.constant.SqlOperator;
027import com.mybatisflex.core.dialect.IDialect;
028import com.mybatisflex.core.dialect.OperateType;
029import com.mybatisflex.core.exception.FlexExceptions;
030import com.mybatisflex.core.exception.locale.LocalizedFormats;
031import com.mybatisflex.core.logicdelete.LogicDeleteManager;
032import com.mybatisflex.core.mybatis.TypeHandlerObject;
033import com.mybatisflex.core.query.Brackets;
034import com.mybatisflex.core.query.CPI;
035import com.mybatisflex.core.query.Join;
036import com.mybatisflex.core.query.QueryColumn;
037import com.mybatisflex.core.query.QueryCondition;
038import com.mybatisflex.core.query.QueryMethods;
039import com.mybatisflex.core.query.QueryTable;
040import com.mybatisflex.core.query.QueryWrapper;
041import com.mybatisflex.core.query.SelectQueryColumn;
042import com.mybatisflex.core.query.SelectQueryTable;
043import com.mybatisflex.core.query.SqlOperators;
044import com.mybatisflex.core.query.UnionWrapper;
045import com.mybatisflex.core.row.Row;
046import com.mybatisflex.core.tenant.TenantManager;
047import com.mybatisflex.core.update.RawValue;
048import com.mybatisflex.core.update.UpdateWrapper;
049import com.mybatisflex.core.util.ArrayUtil;
050import com.mybatisflex.core.util.ClassUtil;
051import com.mybatisflex.core.util.CollectionUtil;
052import com.mybatisflex.core.util.ConvertUtil;
053import com.mybatisflex.core.util.EnumWrapper;
054import com.mybatisflex.core.util.FieldWrapper;
055import com.mybatisflex.core.util.MapUtil;
056import com.mybatisflex.core.util.ObjectUtil;
057import com.mybatisflex.core.util.SqlUtil;
058import com.mybatisflex.core.util.StringUtil;
059import org.apache.ibatis.mapping.ResultFlag;
060import org.apache.ibatis.mapping.ResultMap;
061import org.apache.ibatis.mapping.ResultMapping;
062import org.apache.ibatis.reflection.MetaObject;
063import org.apache.ibatis.reflection.Reflector;
064import org.apache.ibatis.reflection.ReflectorFactory;
065import org.apache.ibatis.session.Configuration;
066import org.apache.ibatis.type.TypeHandler;
067
068import java.lang.reflect.Field;
069import java.lang.reflect.Proxy;
070import java.sql.ResultSet;
071import java.sql.SQLException;
072import java.util.ArrayList;
073import java.util.Arrays;
074import java.util.Collections;
075import java.util.HashMap;
076import java.util.HashSet;
077import java.util.LinkedHashMap;
078import java.util.LinkedHashSet;
079import java.util.List;
080import java.util.Map;
081import java.util.Objects;
082import java.util.Set;
083import java.util.StringJoiner;
084import java.util.concurrent.ConcurrentHashMap;
085import java.util.function.Function;
086import java.util.stream.Collectors;
087
088import static com.mybatisflex.core.constant.SqlConsts.AND;
089import static com.mybatisflex.core.constant.SqlConsts.EQUALS_PLACEHOLDER;
090import static com.mybatisflex.core.constant.SqlConsts.IN;
091
092public class TableInfo {
093
094    // column 和 java 属性的称的关系映射
095    private final Map<String, ColumnInfo> columnInfoMapping = new HashMap<>();
096    // property:column
097    private final Map<String, String> propertyColumnMapping = new LinkedHashMap<>();
098    private String schema; // schema
099    private boolean camelToUnderline = true;
100    private String dataSource;
101
102    private String comment;
103    private String tableName; // 表名
104    private Class<?> entityClass; // 实体类
105    // 逻辑删除数据库列名
106    private String logicDeleteColumn;
107    // 乐观锁字段
108    private String versionColumn;
109    // 租户ID 字段
110    private String tenantIdColumn;
111    // 数据插入时,默认插入数据字段
112    private Map<String, String> onInsertColumns;
113
114    private String[] allColumns = new String[0];
115    // 数据更新时,默认更新内容的字段
116    private Map<String, String> onUpdateColumns;
117    // 大字段列
118    private String[] largeColumns = new String[0];
119
120    // 默认查询列,排除 large 等字段
121    private String[] defaultQueryColumns = new String[0];
122    // 所有的字段,但除了主键的列
123    private String[] columns = new String[0];
124
125    private List<ColumnInfo> columnInfoList;
126    private List<IdInfo> primaryKeyList;
127    // 主键字段
128    private String[] primaryColumns = new String[0];
129    private final Map<String, QueryColumn> columnQueryMapping = new HashMap<>();
130    // 在插入数据的时候,支持主动插入的主键字段,自增字段不需要主动插入
131    // 但通过自定义生成器生成 或者 Sequence 在 before 生成的时候,是需要主动插入数据的
132    private String[] insertPrimaryKeys;
133
134    private List<InsertListener> onInsertListeners;
135    private List<UpdateListener> onUpdateListeners;
136    private List<SetListener> onSetListeners;
137
138    /**
139     * 对应 MapperXML 配置文件中 {@code <resultMap>} 标签下的 {@code <association>} 标签。
140     */
141    private Map<String, Class<?>> associationType;
142
143    /**
144     * 对应 MapperXML 配置文件中 {@code <resultMap>} 标签下的 {@code <collection>} 标签。
145     */
146    private Map<Field, Class<?>> collectionType;
147
148
149    private final ReflectorFactory reflectorFactory = new BaseReflectorFactory() {
150        @Override
151        public Reflector findForClass(Class<?> type) {
152            return getReflector();
153        }
154    };
155    private Reflector reflector; // 反射工具
156
157    public String getSchema() {
158        return schema;
159    }
160
161    public void setSchema(String schema) {
162        this.schema = schema;
163    }
164
165    public Map<String, String> getPropertyColumnMapping() {
166        return this.propertyColumnMapping;
167    }
168
169    public String getTableName() {
170        return tableName;
171    }
172
173    public String getTableNameWithSchema() {
174        return StringUtil.buildSchemaWithTable(schema, tableName);
175    }
176
177    public String getWrapSchemaAndTableName(IDialect dialect, OperateType operateType) {
178        if (StringUtil.isNotBlank(schema)) {
179            String table = dialect.getRealTable(tableName, operateType);
180            return dialect.wrap(dialect.getRealSchema(schema, table, operateType)) + "." + dialect.wrap(table);
181        } else {
182            return dialect.wrap(dialect.getRealTable(tableName, operateType));
183        }
184    }
185
186    public void setTableName(String tableName) {
187        int indexOf = tableName.indexOf(".");
188        if (indexOf > 0) {
189            if (StringUtil.isBlank(schema)) {
190                this.schema = tableName.substring(0, indexOf);
191                this.tableName = tableName.substring(indexOf + 1);
192            } else {
193                this.tableName = tableName;
194            }
195        } else {
196            this.tableName = tableName;
197        }
198    }
199
200    public Class<?> getEntityClass() {
201        return entityClass;
202    }
203
204    public void setEntityClass(Class<?> entityClass) {
205        this.entityClass = entityClass;
206    }
207
208    public boolean isCamelToUnderline() {
209        return camelToUnderline;
210    }
211
212    public void setCamelToUnderline(boolean camelToUnderline) {
213        this.camelToUnderline = camelToUnderline;
214    }
215
216    public String getDataSource() {
217        return dataSource;
218    }
219
220    public void setDataSource(String dataSource) {
221        this.dataSource = dataSource;
222    }
223
224    public String getComment() {
225        return comment;
226    }
227
228    public void setComment(String comment) {
229        this.comment = comment;
230    }
231
232    public String getLogicDeleteColumnOrSkip() {
233        return LogicDeleteManager.getLogicDeleteColumn(logicDeleteColumn);
234    }
235
236    public String getLogicDeleteColumn() {
237        return logicDeleteColumn;
238    }
239
240    public void setLogicDeleteColumn(String logicDeleteColumn) {
241        this.logicDeleteColumn = logicDeleteColumn;
242    }
243
244    public String getVersionColumn() {
245        return versionColumn;
246    }
247
248    public void setVersionColumn(String versionColumn) {
249        this.versionColumn = versionColumn;
250    }
251
252    public String getTenantIdColumn() {
253        return tenantIdColumn;
254    }
255
256    public void setTenantIdColumn(String tenantIdColumn) {
257        this.tenantIdColumn = tenantIdColumn;
258    }
259
260    public Map<String, String> getOnInsertColumns() {
261        return onInsertColumns;
262    }
263
264    public void setOnInsertColumns(Map<String, String> onInsertColumns) {
265        this.onInsertColumns = onInsertColumns;
266    }
267
268    public Map<String, String> getOnUpdateColumns() {
269        return onUpdateColumns;
270    }
271
272    public void setOnUpdateColumns(Map<String, String> onUpdateColumns) {
273        this.onUpdateColumns = onUpdateColumns;
274    }
275
276    public String[] getLargeColumns() {
277        return largeColumns;
278    }
279
280    public void setLargeColumns(String[] largeColumns) {
281        this.largeColumns = largeColumns;
282    }
283
284    public String[] getDefaultQueryColumns() {
285        return defaultQueryColumns;
286    }
287
288    public void setDefaultQueryColumns(String[] defaultQueryColumns) {
289        this.defaultQueryColumns = defaultQueryColumns;
290    }
291
292    public String[] getInsertPrimaryKeys() {
293        return insertPrimaryKeys;
294    }
295
296    public void setInsertPrimaryKeys(String[] insertPrimaryKeys) {
297        this.insertPrimaryKeys = insertPrimaryKeys;
298    }
299
300    public Reflector getReflector() {
301        return reflector;
302    }
303
304    public ReflectorFactory getReflectorFactory() {
305        return reflectorFactory;
306    }
307
308    public void setReflector(Reflector reflector) {
309        this.reflector = reflector;
310    }
311
312    public String[] getAllColumns() {
313        return allColumns;
314    }
315
316    public void setAllColumns(String[] allColumns) {
317        this.allColumns = allColumns;
318    }
319
320    public String[] getColumns() {
321        return columns;
322    }
323
324
325    public void setColumns(String[] columns) {
326        this.columns = columns;
327    }
328
329    public String[] getPrimaryColumns() {
330        return primaryColumns;
331    }
332
333    public void setPrimaryColumns(String[] primaryColumns) {
334        this.primaryColumns = primaryColumns;
335    }
336
337
338    public List<InsertListener> getOnInsertListeners() {
339        return onInsertListeners;
340    }
341
342    public void setOnInsertListeners(List<InsertListener> onInsertListeners) {
343        this.onInsertListeners = onInsertListeners;
344    }
345
346    public List<UpdateListener> getOnUpdateListeners() {
347        return onUpdateListeners;
348    }
349
350    public void setOnUpdateListeners(List<UpdateListener> onUpdateListeners) {
351        this.onUpdateListeners = onUpdateListeners;
352    }
353
354    public List<SetListener> getOnSetListeners() {
355        return onSetListeners;
356    }
357
358    public void setOnSetListeners(List<SetListener> onSetListeners) {
359        this.onSetListeners = onSetListeners;
360    }
361
362    public List<ColumnInfo> getColumnInfoList() {
363        return columnInfoList;
364    }
365
366    public String getColumnByProperty(String property) {
367        String column = propertyColumnMapping.get(property);
368        return StringUtil.isNotBlank(column) ? column : property;
369    }
370
371    public Map<String, Class<?>> getAssociationType() {
372        return associationType;
373    }
374
375    public void setAssociationType(Map<String, Class<?>> associationType) {
376        this.associationType = associationType;
377    }
378
379    public void addAssociationType(String fieldName, Class<?> clazz) {
380        if (associationType == null) {
381            associationType = new HashMap<>();
382        }
383        associationType.put(fieldName, clazz);
384    }
385
386    public Map<Field, Class<?>> getCollectionType() {
387        return collectionType;
388    }
389
390    public void setCollectionType(Map<Field, Class<?>> collectionType) {
391        this.collectionType = collectionType;
392    }
393
394    public void addCollectionType(Field field, Class<?> genericClass) {
395        if (collectionType == null) {
396            collectionType = new HashMap<>();
397        }
398        collectionType.put(field, genericClass);
399    }
400
401    void setColumnInfoList(List<ColumnInfo> columnInfoList) {
402        this.columnInfoList = columnInfoList;
403        List<String> columnNames = new ArrayList<>();
404        for (int i = 0; i < columnInfoList.size(); i++) {
405            ColumnInfo columnInfo = columnInfoList.get(i);
406            // 真正的字段(没有做忽略标识)
407            if (!columnInfo.isIgnore()) {
408                columnNames.add(columnInfo.column);
409
410                columnInfoMapping.put(columnInfo.column, columnInfo);
411                propertyColumnMapping.put(columnInfo.property, columnInfo.column);
412
413                String[] alias = columnInfo.getAlias();
414                columnQueryMapping.put(columnInfo.column, new QueryColumn(schema, tableName, columnInfo.column, alias != null && alias.length > 0 ? alias[0] : null));
415            }
416        }
417
418        this.columns = columnNames.toArray(new String[]{});
419        this.allColumns = ArrayUtil.concat(allColumns, columns);
420    }
421
422
423    public List<IdInfo> getPrimaryKeyList() {
424        return primaryKeyList;
425    }
426
427    void setPrimaryKeyList(List<IdInfo> primaryKeyList) {
428        this.primaryKeyList = primaryKeyList;
429        this.primaryColumns = new String[primaryKeyList.size()];
430
431        List<String> insertIdFields = new ArrayList<>();
432
433        for (int i = 0; i < primaryKeyList.size(); i++) {
434            IdInfo idInfo = primaryKeyList.get(i);
435            primaryColumns[i] = idInfo.getColumn();
436
437            if (idInfo.getKeyType() != KeyType.Auto
438                && (idInfo.getBefore() != null && idInfo.getBefore())
439            ) {
440                insertIdFields.add(idInfo.getColumn());
441            }
442
443            columnInfoMapping.put(idInfo.column, idInfo);
444            propertyColumnMapping.put(idInfo.property, idInfo.column);
445
446            String[] alias = idInfo.getAlias();
447            columnQueryMapping.put(idInfo.column, new QueryColumn(schema, tableName, idInfo.column, alias != null && alias.length > 0 ? alias[0] : null));
448        }
449        this.allColumns = ArrayUtil.concat(allColumns, primaryColumns);
450        this.insertPrimaryKeys = insertIdFields.toArray(new String[0]);
451    }
452
453
454    /**
455     * 构建 insert 的 Sql 参数
456     *
457     * @param entity      从 entity 中获取
458     * @param ignoreNulls 是否忽略 null 值
459     * @return 数组
460     */
461    public Object[] buildInsertSqlArgs(Object entity, boolean ignoreNulls) {
462        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
463        String[] insertColumns = obtainInsertColumns(entity, ignoreNulls);
464
465        Map<String, RawValue> rawValueMap = obtainUpdateRawValueMap(entity);
466
467        List<Object> values = new ArrayList<>();
468        for (String insertColumn : insertColumns) {
469            if (onInsertColumns == null || !onInsertColumns.containsKey(insertColumn)) {
470                if (rawValueMap.containsKey(insertColumn)) {
471                    values.addAll(Arrays.asList(rawValueMap.remove(insertColumn).getParams()));
472                    continue;
473                }
474                Object value = buildColumnSqlArg(metaObject, insertColumn);
475                if (ignoreNulls && value == null) {
476                    continue;
477                }
478                values.add(value);
479            }
480        }
481        values.addAll(rawValueMap.values()
482            .stream()
483            .flatMap(e -> Arrays.stream(e.getParams()))
484            .collect(Collectors.toList()));
485        return values.toArray();
486    }
487
488    /**
489     * 插入(新增)数据时,获取所有要插入的字段
490     *
491     * @param entity
492     * @param ignoreNulls
493     * @return 字段列表
494     */
495    public String[] obtainInsertColumns(Object entity, boolean ignoreNulls) {
496        if (!ignoreNulls) {
497            return ArrayUtil.concat(insertPrimaryKeys, columns);
498        }
499        // 忽略 null 字段,
500        else {
501            MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
502            List<String> retColumns = new ArrayList<>();
503            for (String insertColumn : allColumns) {
504                if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
505                    retColumns.add(insertColumn);
506                } else {
507                    Object value = buildColumnSqlArg(metaObject, insertColumn);
508                    if (value == null) {
509                        continue;
510                    }
511                    retColumns.add(insertColumn);
512                }
513            }
514            return retColumns.toArray(new String[0]);
515        }
516    }
517
518
519    public Object[] buildInsertSqlArgsWithPk(Object entity, boolean ignoreNulls) {
520        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
521        String[] insertColumns = obtainInsertColumnsWithPk(entity, ignoreNulls);
522
523        List<Object> values = new ArrayList<>(insertColumns.length);
524        for (String insertColumn : insertColumns) {
525            if (onInsertColumns == null || !onInsertColumns.containsKey(insertColumn)) {
526                Object value = buildColumnSqlArg(metaObject, insertColumn);
527                if (ignoreNulls && value == null) {
528                    continue;
529                }
530                values.add(value);
531            }
532        }
533        return values.toArray();
534    }
535
536
537    /**
538     * 插入(新增)数据时,获取所有要插入的字段
539     *
540     * @param entity
541     * @param ignoreNulls
542     * @return 字段列表
543     */
544    public String[] obtainInsertColumnsWithPk(Object entity, boolean ignoreNulls) {
545        if (!ignoreNulls) {
546            return allColumns;
547        } else {
548            MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
549            List<String> retColumns = new ArrayList<>();
550            for (String primaryKey : primaryColumns) {
551                Object value = buildColumnSqlArg(metaObject, primaryKey);
552                if (value == null) {
553                    throw new IllegalArgumentException("Entity Primary Key value must not be null.");
554                }
555                retColumns.add(primaryKey);
556            }
557            for (String insertColumn : columns) {
558                if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
559                    retColumns.add(insertColumn);
560                } else {
561                    Object value = buildColumnSqlArg(metaObject, insertColumn);
562                    if (value == null) {
563                        continue;
564                    }
565                    retColumns.add(insertColumn);
566                }
567            }
568            return retColumns.toArray(new String[0]);
569        }
570    }
571
572
573    @SuppressWarnings({"unchecked", "rawtypes"})
574    public Map<String, RawValue> obtainUpdateRawValueMap(Object entity) {
575        if (!(entity instanceof UpdateWrapper)) {
576            return Collections.emptyMap();
577        }
578
579        Map<String, Object> updates = ((UpdateWrapper) entity).getUpdates();
580        if (updates.isEmpty()) {
581            return Collections.emptyMap();
582        }
583
584        Map<String, RawValue> map = new HashMap<>();
585        updates.forEach((key, value) -> {
586            if (value instanceof RawValue) {
587                String column = getColumnByProperty(key);
588                map.put(column, (RawValue) value);
589            }
590        });
591
592        return map;
593    }
594
595    /**
596     * 获取要修改的值
597     *
598     * @param entity
599     * @param ignoreNulls
600     */
601    public Set<String> obtainUpdateColumns(Object entity, boolean ignoreNulls, boolean includePrimary) {
602        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
603        Set<String> columns = new LinkedHashSet<>(); // 需使用 LinkedHashSet 保证 columns 的顺序
604        if (entity instanceof UpdateWrapper) {
605            Map<String, Object> updates = ((UpdateWrapper) entity).getUpdates();
606            if (updates.isEmpty()) {
607                return Collections.emptySet();
608            }
609            for (String property : updates.keySet()) {
610
611                String column = getColumnByProperty(property);
612
613                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
614                    continue;
615                }
616
617                // 过滤乐观锁字段 和 租户字段
618                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
619                    continue;
620                }
621
622                if (!includePrimary && ArrayUtil.contains(primaryColumns, column)) {
623                    continue;
624                }
625
626//                Object value = updates.get(property);
627                // ModifyAttrsRecord 忽略 ignoreNulls 的设置
628                // Object value = getPropertyValue(metaObject, property);
629                // if (ignoreNulls && value == null) {
630                //     continue;
631                // }
632                columns.add(column);
633            }
634        }
635        // not ModifyAttrsRecord
636        else {
637            for (String column : this.columns) {
638                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
639                    continue;
640                }
641
642                // 过滤乐观锁字段 和 租户字段
643                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
644                    continue;
645                }
646
647                Object value = buildColumnSqlArg(metaObject, column);
648                if (ignoreNulls && value == null) {
649                    continue;
650                }
651
652                columns.add(column);
653            }
654        }
655        return columns;
656    }
657
658    /**
659     * 获取所有要修改的值,默认为全部除了主键以外的字段
660     *
661     * @param entity 实体对象
662     * @return 数组
663     */
664    public Object[] buildUpdateSqlArgs(Object entity, boolean ignoreNulls, boolean includePrimary) {
665
666        List<Object> values = new ArrayList<>();
667        if (entity instanceof UpdateWrapper) {
668            Map<String, Object> updates = ((UpdateWrapper) entity).getUpdates();
669            if (updates.isEmpty()) {
670                return FlexConsts.EMPTY_ARRAY;
671            }
672            for (String property : updates.keySet()) {
673
674                String column = getColumnByProperty(property);
675
676                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
677                    continue;
678                }
679                // 过滤乐观锁字段 和 租户字段
680                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
681                    continue;
682                }
683
684                if (!includePrimary && ArrayUtil.contains(primaryColumns, column)) {
685                    continue;
686                }
687
688                Object value = updates.get(property);
689                if (value instanceof RawValue) {
690                    values.addAll(Arrays.asList(((RawValue) value).getParams()));
691                    continue;
692                }
693
694                if (value != null) {
695                    ColumnInfo columnInfo = columnInfoMapping.get(column);
696                    if (columnInfo != null) {
697                        TypeHandler typeHandler = columnInfo.buildTypeHandler(null);
698                        if (typeHandler != null) {
699                            value = new TypeHandlerObject(typeHandler, value, columnInfo.getJdbcType());
700                        }
701                    }
702
703                    // fixed: https://gitee.com/mybatis-flex/mybatis-flex/issues/I7TFBK
704                    if (value.getClass().isEnum()) {
705                        EnumWrapper enumWrapper = EnumWrapper.of(value.getClass());
706                        value = enumWrapper.getEnumValue((Enum) value);
707                    }
708                }
709
710                // ModifyAttrsRecord 忽略 ignoreNulls 的设置,
711                // 当使用 ModifyAttrsRecord 时,可以理解为要对字段进行 null 值进行更新,否则没必要使用 ModifyAttrsRecord
712                // if (ignoreNulls && value == null) {
713                //    continue;
714                // }
715                values.add(value);
716            }
717        }
718        // normal entity. not ModifyAttrsRecord
719        else {
720            MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
721
722            for (String column : this.columns) {
723                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
724                    continue;
725                }
726
727                // 过滤乐观锁字段 和 租户字段
728                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
729                    continue;
730                }
731
732                // 普通 entity 忽略 includePrimary 的设置,
733                // 因为 for 循环中的 this.columns 本身就不包含有主键
734                // if (includePrimary) {
735                // }
736
737                Object value = buildColumnSqlArg(metaObject, column);
738                if (ignoreNulls && value == null) {
739                    continue;
740                }
741
742                values.add(value);
743            }
744        }
745
746        return values.toArray();
747    }
748
749
750    /**
751     * 构建主键的 sql 参数数据
752     *
753     * @param entity
754     */
755    public Object[] buildPkSqlArgs(Object entity) {
756        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
757        Object[] values = new Object[primaryColumns.length];
758        for (int i = 0; i < primaryColumns.length; i++) {
759            values[i] = buildColumnSqlArg(metaObject, primaryColumns[i]);
760        }
761        return values;
762    }
763
764    public Object getValue(Object entity, String property) {
765        FieldWrapper fieldWrapper = FieldWrapper.of(entityClass, property);
766        return fieldWrapper.get(entity);
767    }
768
769    /**
770     * 获取主键值
771     *
772     * @param entity
773     * @return 主键值,有多个主键时返回数组
774     */
775    public Object getPkValue(Object entity) {
776        // 绝大多数情况为 1 个主键
777        if (primaryColumns.length == 1) {
778            MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
779            ColumnInfo columnInfo = columnInfoMapping.get(primaryColumns[0]);
780            return getPropertyValue(metaObject, columnInfo.property);
781        }
782        // 多个主键
783        else if (primaryColumns.length > 1) {
784            MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
785            Object[] values = new Object[primaryColumns.length];
786            for (int i = 0; i < primaryColumns.length; i++) {
787                ColumnInfo columnInfo = columnInfoMapping.get(primaryColumns[i]);
788                values[i] = getPropertyValue(metaObject, columnInfo.property);
789            }
790            return values;
791        }
792        // 无主键
793        else {
794            return null;
795        }
796    }
797
798
799    public Object[] buildTenantIdArgs() {
800        if (StringUtil.isBlank(tenantIdColumn)) {
801            return null;
802        }
803
804        return TenantManager.getTenantIds(tableName);
805    }
806
807
808    public String buildTenantCondition(String sql, Object[] tenantIdArgs, IDialect dialect) {
809        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
810            if (tenantIdArgs.length == 1) {
811                return sql + AND + dialect.wrap(tenantIdColumn) + EQUALS_PLACEHOLDER;
812            } else {
813                return sql + AND + dialect.wrap(tenantIdColumn) + IN + SqlUtil.buildSqlParamPlaceholder(tenantIdArgs.length);
814            }
815        } else {
816            return sql;
817        }
818    }
819
820    public void buildTenantCondition(StringBuilder sql, Object[] tenantIdArgs, IDialect dialect) {
821        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
822            if (tenantIdArgs.length == 1) {
823                sql.append(AND).append(dialect.wrap(tenantIdColumn)).append(EQUALS_PLACEHOLDER);
824            } else {
825                sql.append(AND).append(dialect.wrap(tenantIdColumn)).append(IN).append(SqlUtil.buildSqlParamPlaceholder(tenantIdArgs.length));
826            }
827        }
828    }
829
830
831    public void buildTenantCondition(QueryWrapper queryWrapper) {
832        Object[] tenantIdArgs = buildTenantIdArgs();
833        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
834            if (tenantIdArgs.length == 1) {
835                queryWrapper.where(QueryCondition.create(schema, tableName, tenantIdColumn, SqlConsts.EQUALS, tenantIdArgs[0]));
836            } else {
837                queryWrapper.where(QueryCondition.create(schema, tableName, tenantIdColumn, SqlConsts.IN, tenantIdArgs));
838            }
839        }
840    }
841
842
843    private static final String APPEND_CONDITIONS_FLAG = "appendConditions";
844
845    public void appendConditions(Object entity, QueryWrapper queryWrapper) {
846
847        Object appendConditions = CPI.getContext(queryWrapper, APPEND_CONDITIONS_FLAG);
848        if (Boolean.TRUE.equals(appendConditions)) {
849            return;
850        } else {
851            CPI.putContext(queryWrapper, APPEND_CONDITIONS_FLAG, Boolean.TRUE);
852        }
853
854        // select xxx.id,(select..) from xxx
855        List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
856        if (selectColumns != null && !selectColumns.isEmpty()) {
857            for (QueryColumn queryColumn : selectColumns) {
858                if (queryColumn instanceof SelectQueryColumn) {
859                    QueryWrapper selectColumnQueryWrapper = CPI.getQueryWrapper((SelectQueryColumn) queryColumn);
860                    doAppendConditions(entity, selectColumnQueryWrapper);
861                }
862            }
863        }
864
865        // select * from (select ... from ) 中的子查询处理
866        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
867        if (queryTables != null && !queryTables.isEmpty()) {
868            for (QueryTable queryTable : queryTables) {
869                if (queryTable instanceof SelectQueryTable) {
870                    QueryWrapper selectQueryWrapper = ((SelectQueryTable) queryTable).getQueryWrapper();
871                    doAppendConditions(entity, selectQueryWrapper);
872                }
873            }
874        }
875
876        // 添加乐观锁条件,只有在 update 的时候进行处理
877        if (StringUtil.isNotBlank(versionColumn) && entity != null) {
878            Object versionValue = buildColumnSqlArg(entity, versionColumn);
879            if (versionValue == null) {
880                throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);
881            }
882            queryWrapper.and(QueryCondition.create(schema, tableName, versionColumn, SqlConsts.EQUALS, versionValue));
883        }
884
885        // 逻辑删除
886        if (StringUtil.isNotBlank(getLogicDeleteColumnOrSkip())) {
887            // 逻辑删除时 保证前面的条件被括号包裹
888            // fix:https://gitee.com/mybatis-flex/mybatis-flex/issues/I9163G
889            QueryCondition whereCondition = CPI.getWhereQueryCondition(queryWrapper);
890            if (whereCondition != null && !(whereCondition instanceof Brackets)) {
891                QueryCondition wrappedCondition = new Brackets(whereCondition);
892                CPI.setWhereQueryCondition(queryWrapper, wrappedCondition);
893            }
894
895            String joinTableAlias = CPI.getContext(queryWrapper, "joinTableAlias");
896            LogicDeleteManager.getProcessor().buildQueryCondition(queryWrapper, this, joinTableAlias);
897        }
898
899        // 多租户
900        buildTenantCondition(queryWrapper);
901
902
903        // 子查询
904        List<QueryWrapper> childSelects = CPI.getChildSelect(queryWrapper);
905        if (CollectionUtil.isNotEmpty(childSelects)) {
906            for (QueryWrapper childQueryWrapper : childSelects) {
907                doAppendConditions(entity, childQueryWrapper);
908            }
909        }
910
911
912        // join
913        List<Join> joins = CPI.getJoins(queryWrapper);
914        if (CollectionUtil.isNotEmpty(joins)) {
915            for (Join join : joins) {
916                QueryTable joinQueryTable = CPI.getJoinQueryTable(join);
917
918                // join select
919                if (joinQueryTable instanceof SelectQueryTable) {
920                    QueryWrapper childQuery = ((SelectQueryTable) joinQueryTable).getQueryWrapper();
921                    doAppendConditions(entity, childQuery);
922                }
923                // join table
924                else {
925                    String nameWithSchema = joinQueryTable.getNameWithSchema();
926                    if (StringUtil.isNotBlank(nameWithSchema)) {
927                        TableInfo tableInfo = TableInfoFactory.ofTableName(nameWithSchema);
928                        if (tableInfo != null) {
929                            QueryCondition joinQueryCondition = CPI.getJoinQueryCondition(join);
930                            QueryWrapper newWrapper = QueryWrapper.create()
931                                .where(joinQueryCondition);
932                            CPI.putContext(newWrapper, "joinTableAlias", joinQueryTable.getAlias());
933                            tableInfo.appendConditions(entity, newWrapper);
934                            QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(newWrapper);
935                            CPI.setJoinQueryCondition(join, whereQueryCondition);
936                        }
937                    }
938                }
939            }
940        }
941
942        // union
943        List<UnionWrapper> unions = CPI.getUnions(queryWrapper);
944        if (CollectionUtil.isNotEmpty(unions)) {
945            for (UnionWrapper union : unions) {
946                QueryWrapper unionQueryWrapper = union.getQueryWrapper();
947                doAppendConditions(entity, unionQueryWrapper);
948            }
949        }
950    }
951
952
953    private void doAppendConditions(Object entity, QueryWrapper queryWrapper) {
954        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
955        if (queryTables != null && !queryTables.isEmpty()) {
956            for (QueryTable queryTable : queryTables) {
957                if (queryTable instanceof SelectQueryTable) {
958                    QueryWrapper childQuery = ((SelectQueryTable) queryTable).getQueryWrapper();
959                    doAppendConditions(entity, childQuery);
960                } else {
961                    String nameWithSchema = queryTable.getNameWithSchema();
962                    if (StringUtil.isNotBlank(nameWithSchema)) {
963                        TableInfo tableInfo = TableInfoFactory.ofTableName(nameWithSchema);
964                        if (tableInfo != null) {
965                            tableInfo.appendConditions(entity, queryWrapper);
966                        }
967                    }
968                }
969            }
970        }
971    }
972
973
974    public QueryWrapper buildQueryWrapper(Object entity, SqlOperators operators) {
975        QueryColumn[] queryColumns = new QueryColumn[defaultQueryColumns.length];
976        for (int i = 0; i < defaultQueryColumns.length; i++) {
977            queryColumns[i] = columnQueryMapping.get(defaultQueryColumns[i]);
978        }
979
980        QueryWrapper queryWrapper = QueryWrapper.create();
981
982        String tableNameWithSchema = getTableNameWithSchema();
983        queryWrapper.select(queryColumns).from(tableNameWithSchema);
984
985        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
986        propertyColumnMapping.forEach((property, column) -> {
987            if (column.equals(logicDeleteColumn)) {
988                return;
989            }
990            Object value = metaObject.getValue(property);
991            if (value != null && !"".equals(value)) {
992                QueryColumn queryColumn = Arrays.stream(queryColumns)
993                    .filter(e -> e.getName().equals(column))
994                    .findFirst()
995                    .orElse(QueryMethods.column(getTableNameWithSchema(), column));
996                if (operators != null) {
997                    SqlOperator operator = operators.get(property);
998                    if (operator == null) {
999                        operator = SqlOperator.EQUALS;
1000                    } else if (operator == SqlOperator.IGNORE) {
1001                        return;
1002                    }
1003                    if (operator == SqlOperator.LIKE || operator == SqlOperator.NOT_LIKE) {
1004                        value = "%" + value + "%";
1005                    } else if (operator == SqlOperator.LIKE_LEFT || operator == SqlOperator.NOT_LIKE_LEFT) {
1006                        value = value + "%";
1007                    } else if (operator == SqlOperator.LIKE_RIGHT || operator == SqlOperator.NOT_LIKE_RIGHT) {
1008                        value = "%" + value;
1009                    }
1010                    queryWrapper.and(QueryCondition.create(queryColumn, operator, value));
1011                } else {
1012                    queryWrapper.and(queryColumn.eq(value));
1013                }
1014            }
1015        });
1016        return queryWrapper;
1017    }
1018
1019    public String getKeyProperties() {
1020        StringJoiner joiner = new StringJoiner(",");
1021        for (IdInfo value : primaryKeyList) {
1022            joiner.add(FlexConsts.ENTITY + "." + value.getProperty());
1023        }
1024        return joiner.toString();
1025    }
1026
1027
1028    public String getKeyColumns() {
1029        StringJoiner joiner = new StringJoiner(",");
1030        for (IdInfo value : primaryKeyList) {
1031            joiner.add(value.getColumn());
1032        }
1033        return joiner.toString();
1034    }
1035
1036    public List<QueryColumn> getDefaultQueryColumn() {
1037        return Arrays.stream(defaultQueryColumns)
1038            .map(columnQueryMapping::get)
1039            .collect(Collectors.toList());
1040    }
1041
1042    private void getCombinedColumns(List<Class<?>> existedEntities, Class<?> entityClass, List<String> combinedColumns) {
1043        if (existedEntities.contains(entityClass)) {
1044            return;
1045        }
1046
1047        existedEntities.add(entityClass);
1048
1049        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass);
1050
1051        combinedColumns.addAll(Arrays.asList(tableInfo.allColumns));
1052
1053        if (tableInfo.collectionType != null) {
1054            tableInfo.collectionType.values()
1055                .forEach(e -> getCombinedColumns(existedEntities, e, combinedColumns));
1056        }
1057
1058        if (tableInfo.associationType != null) {
1059            tableInfo.associationType.values()
1060                .forEach(e -> getCombinedColumns(existedEntities, e, combinedColumns));
1061        }
1062    }
1063
1064    public ResultMap buildResultMap(Configuration configuration) {
1065        List<String> combinedColumns = new ArrayList<>();
1066
1067        getCombinedColumns(new ArrayList<>(), entityClass, combinedColumns);
1068
1069        // 预加载所有重复列,用于判断重名属性
1070        List<String> existedColumns = combinedColumns.stream()
1071            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
1072            .entrySet()
1073            .stream()
1074            .filter(e -> e.getValue().intValue() > 1)
1075            .map(Map.Entry::getKey)
1076            .collect(Collectors.toList());
1077
1078        return doBuildResultMap(configuration, new HashSet<>(), existedColumns, false, getTableNameWithSchema());
1079    }
1080
1081    private ResultMap doBuildResultMap(Configuration configuration, Set<String> resultMapIds, List<String> existedColumns, boolean isNested, String nestedPrefix) {
1082
1083        String resultMapId = isNested ? "nested-" + nestedPrefix + ":" + entityClass.getName() : entityClass.getName();
1084
1085        // 是否有循环引用
1086        boolean withCircularReference = resultMapIds.contains(resultMapId) || resultMapIds.contains(entityClass.getName());
1087        if (withCircularReference) {
1088            return null;
1089        }
1090
1091        resultMapIds.add(resultMapId);
1092
1093        if (configuration.hasResultMap(resultMapId)) {
1094            return configuration.getResultMap(resultMapId);
1095        }
1096
1097        List<ResultMapping> resultMappings = new ArrayList<>();
1098
1099        // <resultMap> 标签下的 <id> 标签映射
1100        for (IdInfo idInfo : primaryKeyList) {
1101            doBuildColumnResultMapping(configuration, resultMappings, existedColumns, idInfo, CollectionUtil.newArrayList(ResultFlag.ID), isNested);
1102        }
1103
1104        // <resultMap> 标签下的 <result> 标签映射
1105        for (ColumnInfo columnInfo : columnInfoList) {
1106            doBuildColumnResultMapping(configuration, resultMappings, existedColumns, columnInfo, Collections.emptyList(), isNested);
1107        }
1108
1109        // <resultMap> 标签下的 <association> 标签映射
1110        if (associationType != null) {
1111            associationType.forEach((fieldName, fieldType) -> {
1112                // 获取嵌套类型的信息,也就是 javaType 属性
1113                TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType);
1114                // 构建嵌套类型的 ResultMap 对象,也就是 <association> 标签下的内容
1115                ResultMap nestedResultMap = tableInfo.doBuildResultMap(configuration, resultMapIds, existedColumns, true, nestedPrefix);
1116                if (nestedResultMap != null) {
1117                    resultMappings.add(new ResultMapping.Builder(configuration, fieldName)
1118                        .javaType(fieldType)
1119                        .nestedResultMapId(nestedResultMap.getId())
1120                        .build());
1121                }
1122            });
1123        }
1124
1125        // <resultMap> 标签下的 <collection> 标签映射
1126        if (collectionType != null) {
1127            collectionType.forEach((field, genericClass) -> {
1128                if (TableInfoFactory.defaultSupportColumnTypes.contains(genericClass)) {
1129                    // List<String> List<Integer> 等
1130                    String columnName = TableInfoFactory.getColumnName(camelToUnderline, field, field.getAnnotation(Column.class));
1131                    // 映射 <result column="..."/>
1132                    ResultMapping resultMapping = new ResultMapping.Builder(configuration, null)
1133                        .column(columnName)
1134                        .typeHandler(configuration.getTypeHandlerRegistry().getTypeHandler(genericClass))
1135                        .build();
1136                    String nestedResultMapId = entityClass.getName() + "." + field.getName();
1137                    ResultMap nestedResultMap;
1138                    if (configuration.hasResultMap(nestedResultMapId)) {
1139                        nestedResultMap = configuration.getResultMap(nestedResultMapId);
1140                    } else {
1141                        nestedResultMap = new ResultMap.Builder(configuration, nestedResultMapId, genericClass, Collections.singletonList(resultMapping)).build();
1142                        configuration.addResultMap(nestedResultMap);
1143                    }
1144                    // 映射 <collection property="..." ofType="genericClass">
1145                    resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
1146                        .javaType(field.getType())
1147                        .nestedResultMapId(nestedResultMap.getId())
1148                        .build());
1149                } else {
1150                    // 获取集合泛型类型的信息,也就是 ofType 属性
1151                    TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass);
1152                    // 构建嵌套类型的 ResultMap 对象,也就是 <collection> 标签下的内容
1153                    ResultMap nestedResultMap = tableInfo.doBuildResultMap(configuration, resultMapIds, existedColumns, true, nestedPrefix);
1154                    if (nestedResultMap != null) {
1155                        resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
1156                            .javaType(field.getType())
1157                            .nestedResultMapId(nestedResultMap.getId())
1158                            .build());
1159                    }
1160                }
1161            });
1162        }
1163
1164        ResultMap resultMap = new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
1165        configuration.addResultMap(resultMap);
1166        resultMapIds.add(resultMapId);
1167        return resultMap;
1168    }
1169
1170    private void doBuildColumnResultMapping(Configuration configuration, List<ResultMapping> resultMappings
1171        , List<String> existedColumns, ColumnInfo columnInfo, List<ResultFlag> flags, boolean isNested) {
1172
1173        if (!isNested) {
1174            if (existedColumns.contains(columnInfo.column)) {
1175                // userName -> tb_user$user_name
1176                resultMappings.add(new ResultMapping.Builder(configuration
1177                    , columnInfo.property
1178                    , tableName + "$" + columnInfo.column
1179                    , columnInfo.propertyType)
1180                    .jdbcType(columnInfo.getJdbcType())
1181                    .flags(flags)
1182                    .typeHandler(columnInfo.buildTypeHandler(configuration))
1183                    .build());
1184            }
1185            buildDefaultResultMapping(configuration, resultMappings, columnInfo, flags);
1186        } else {
1187            if (existedColumns.contains(columnInfo.column)) {
1188                // userName -> tb_user$user_name
1189                resultMappings.add(new ResultMapping.Builder(configuration
1190                    , columnInfo.property
1191                    , tableName + "$" + columnInfo.column
1192                    , columnInfo.propertyType)
1193                    .jdbcType(columnInfo.getJdbcType())
1194                    .flags(flags)
1195                    .typeHandler(columnInfo.buildTypeHandler(configuration))
1196                    .build());
1197            } else {
1198                buildDefaultResultMapping(configuration, resultMappings, columnInfo, flags);
1199            }
1200        }
1201
1202        if (ArrayUtil.isNotEmpty(columnInfo.alias)) {
1203            for (String alias : columnInfo.alias) {
1204                // userName -> alias
1205                resultMappings.add(new ResultMapping.Builder(configuration
1206                    , columnInfo.property
1207                    , alias
1208                    , columnInfo.propertyType)
1209                    .jdbcType(columnInfo.getJdbcType())
1210                    .flags(flags)
1211                    .typeHandler(columnInfo.buildTypeHandler(configuration))
1212                    .build());
1213            }
1214        }
1215
1216    }
1217
1218    private void buildDefaultResultMapping(Configuration configuration, List<ResultMapping> resultMappings, ColumnInfo columnInfo, List<ResultFlag> flags) {
1219        // userName -> user_name
1220        resultMappings.add(new ResultMapping.Builder(configuration
1221            , columnInfo.property
1222            , columnInfo.column
1223            , columnInfo.propertyType)
1224            .jdbcType(columnInfo.getJdbcType())
1225            .flags(flags)
1226            .typeHandler(columnInfo.buildTypeHandler(configuration))
1227            .build());
1228
1229        if (!Objects.equals(columnInfo.column, columnInfo.property)) {
1230            // userName -> userName
1231            resultMappings.add(new ResultMapping.Builder(configuration
1232                , columnInfo.property
1233                , columnInfo.property
1234                , columnInfo.propertyType)
1235                .jdbcType(columnInfo.getJdbcType())
1236                .flags(flags)
1237                .typeHandler(columnInfo.buildTypeHandler(configuration))
1238                .build());
1239        }
1240    }
1241
1242
1243    private Object buildColumnSqlArg(MetaObject metaObject, String column) {
1244        ColumnInfo columnInfo = columnInfoMapping.get(column);
1245        Object value = getPropertyValue(metaObject, columnInfo.property);
1246        if (value != null) {
1247            TypeHandler<?> typeHandler = columnInfo.buildTypeHandler(null);
1248            if (typeHandler != null) {
1249                return new TypeHandlerObject(typeHandler, value, columnInfo.getJdbcType());
1250            }
1251        }
1252        return value;
1253    }
1254
1255
1256    public Object buildColumnSqlArg(Object entityObject, String column) {
1257        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
1258        return buildColumnSqlArg(metaObject, column);
1259    }
1260
1261
1262    public Object getPropertyValue(MetaObject metaObject, String property) {
1263        if (property != null && metaObject.hasGetter(property)) {
1264            return metaObject.getValue(property);
1265        }
1266        return null;
1267    }
1268
1269
1270    /**
1271     * 通过 row 实例类转换为一个 entity
1272     *
1273     * @return entity
1274     */
1275    public <T> T newInstanceByRow(Row row, int index) {
1276        Object instance = ClassUtil.newInstance(entityClass);
1277        MetaObject metaObject = EntityMetaObject.forObject(instance, reflectorFactory);
1278        Set<String> rowKeys = row.keySet();
1279        columnInfoMapping.forEach((column, columnInfo) -> {
1280            if (index <= 0) {
1281                String replace = column.replace("_", "");
1282                for (String rowKey : rowKeys) {
1283                    // 修复: 开启 mapUnderscoreToCamelCase = true 时, row 无法转换 entity 的问题
1284                    if (rowKey.equalsIgnoreCase(column) || rowKey.equalsIgnoreCase(replace)) {
1285                        setInstancePropertyValue(row, instance, metaObject, columnInfo, rowKey);
1286                    }
1287                }
1288            } else {
1289                for (int i = index; i >= 0; i--) {
1290                    String newColumn = i <= 0 ? column : column + "$" + i;
1291                    boolean fillValue = false;
1292                    String replace = column.replace("_", "");
1293                    for (String rowKey : rowKeys) {
1294                        // 修复: 开启 mapUnderscoreToCamelCase = true 时, row 无法转换 entity 的问题
1295                        if (rowKey.equalsIgnoreCase(newColumn) || rowKey.equalsIgnoreCase(replace)) {
1296                            setInstancePropertyValue(row, instance, metaObject, columnInfo, rowKey);
1297                            fillValue = true;
1298                            break;
1299                        }
1300                    }
1301                    if (fillValue) {
1302                        break;
1303                    }
1304                }
1305            }
1306        });
1307        // noinspection unchecked
1308        return (T) instance;
1309    }
1310
1311
1312    private void setInstancePropertyValue(Row row, Object instance, MetaObject metaObject, ColumnInfo columnInfo, String rowKey) {
1313        Object rowValue = row.get(rowKey);
1314        TypeHandler<?> typeHandler = columnInfo.buildTypeHandler(null);
1315        if (typeHandler != null) {
1316            try {
1317                // 通过 typeHandler 转换数据
1318                rowValue = typeHandler.getResult(getResultSet(rowValue), 0);
1319            } catch (SQLException e) {
1320                // ignore
1321            }
1322        }
1323        if (rowValue != null && !metaObject.getSetterType(columnInfo.property).isAssignableFrom(rowValue.getClass())) {
1324            rowValue = ConvertUtil.convert(rowValue, metaObject.getSetterType(columnInfo.property), true);
1325        }
1326        rowValue = invokeOnSetListener(instance, columnInfo.property, rowValue);
1327        metaObject.setValue(columnInfo.property, rowValue);
1328    }
1329
1330
1331    private ResultSet getResultSet(Object value) {
1332        return (ResultSet) Proxy.newProxyInstance(TableInfo.class.getClassLoader(),
1333            new Class[]{ResultSet.class}, (proxy, method, args) -> value);
1334    }
1335
1336
1337    /**
1338     * 初始化乐观锁版本号
1339     *
1340     * @param entityObject
1341     */
1342    public void initVersionValueIfNecessary(Object entityObject) {
1343        if (StringUtil.isBlank(versionColumn)) {
1344            return;
1345        }
1346
1347        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
1348        Object columnValue = getPropertyValue(metaObject, columnInfoMapping.get(versionColumn).property);
1349        if (columnValue == null) {
1350            String name = columnInfoMapping.get(versionColumn).property;
1351            Class<?> clazz = metaObject.getSetterType(name);
1352            metaObject.setValue(name, ConvertUtil.convert(0L, clazz));
1353        }
1354    }
1355
1356    /**
1357     * 设置租户id
1358     *
1359     * @param entityObject
1360     */
1361    public void initTenantIdIfNecessary(Object entityObject) {
1362        if (StringUtil.isBlank(tenantIdColumn)) {
1363            return;
1364        }
1365
1366        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
1367        Object[] tenantIds = TenantManager.getTenantIds(tableName);
1368        if (tenantIds == null || tenantIds.length == 0) {
1369            return;
1370        }
1371
1372        // 默认使用第一个作为插入的租户ID
1373        Object tenantId = tenantIds[0];
1374        if (tenantId != null) {
1375            String property = columnInfoMapping.get(tenantIdColumn).property;
1376            Class<?> setterType = metaObject.getSetterType(property);
1377            metaObject.setValue(property, ConvertUtil.convert(tenantId, setterType));
1378        }
1379    }
1380
1381    /**
1382     * 初始化逻辑删除的默认值
1383     *
1384     * @param entityObject
1385     */
1386    public void initLogicDeleteValueIfNecessary(Object entityObject) {
1387        if (StringUtil.isBlank(getLogicDeleteColumnOrSkip())) {
1388            return;
1389        }
1390
1391        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
1392        ColumnInfo logicDeleteColumn = columnInfoMapping.get(this.logicDeleteColumn);
1393        Object columnValue = getPropertyValue(metaObject, logicDeleteColumn.property);
1394        if (columnValue == null) {
1395            Object normalValueOfLogicDelete = LogicDeleteManager.getProcessor().getLogicNormalValue();
1396            if (normalValueOfLogicDelete != null) {
1397                String property = logicDeleteColumn.property;
1398                Class<?> setterType = metaObject.getSetterType(property);
1399                metaObject.setValue(property, ConvertUtil.convert(normalValueOfLogicDelete, setterType));
1400            }
1401        }
1402    }
1403
1404
1405    private static final Map<Class<?>, List<InsertListener>> insertListenerCache = new ConcurrentHashMap<>();
1406
1407    public void invokeOnInsertListener(Object entity) {
1408        List<InsertListener> listeners = MapUtil.computeIfAbsent(insertListenerCache, entityClass, aClass -> {
1409            List<InsertListener> globalListeners = FlexGlobalConfig.getDefaultConfig()
1410                .getSupportedInsertListener(entityClass);
1411            List<InsertListener> allListeners = CollectionUtil.merge(onInsertListeners, globalListeners);
1412            Collections.sort(allListeners);
1413            return allListeners;
1414        });
1415        listeners.forEach(insertListener -> insertListener.onInsert(entity));
1416    }
1417
1418
1419    private static final Map<Class<?>, List<UpdateListener>> updateListenerCache = new ConcurrentHashMap<>();
1420
1421    public void invokeOnUpdateListener(Object entity) {
1422        List<UpdateListener> listeners = MapUtil.computeIfAbsent(updateListenerCache, entityClass, aClass -> {
1423            List<UpdateListener> globalListeners = FlexGlobalConfig.getDefaultConfig()
1424                .getSupportedUpdateListener(entityClass);
1425            List<UpdateListener> allListeners = CollectionUtil.merge(onUpdateListeners, globalListeners);
1426            Collections.sort(allListeners);
1427            return allListeners;
1428        });
1429        listeners.forEach(insertListener -> insertListener.onUpdate(entity));
1430    }
1431
1432
1433    private static final Map<Class<?>, List<SetListener>> setListenerCache = new ConcurrentHashMap<>();
1434
1435    public Object invokeOnSetListener(Object entity, String property, Object value) {
1436        List<SetListener> listeners = MapUtil.computeIfAbsent(setListenerCache, entityClass, aClass -> {
1437            List<SetListener> globalListeners = FlexGlobalConfig.getDefaultConfig()
1438                .getSupportedSetListener(entityClass);
1439            List<SetListener> allListeners = CollectionUtil.merge(onSetListeners, globalListeners);
1440            Collections.sort(allListeners);
1441            return allListeners;
1442        });
1443        for (SetListener setListener : listeners) {
1444            value = setListener.onSet(entity, property, value);
1445        }
1446        return value;
1447    }
1448
1449    public QueryColumn getQueryColumnByProperty(String property) {
1450        String column = getColumnByProperty(property);
1451        return columnQueryMapping.get(column);
1452    }
1453
1454}