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