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