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