001/**
002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package com.mybatisflex.core.table;
017
018import com.mybatisflex.annotation.InsertListener;
019import com.mybatisflex.annotation.KeyType;
020import com.mybatisflex.annotation.SetListener;
021import com.mybatisflex.annotation.UpdateListener;
022import com.mybatisflex.core.FlexConsts;
023import com.mybatisflex.core.FlexGlobalConfig;
024import com.mybatisflex.core.exception.FlexExceptions;
025import com.mybatisflex.core.javassist.ModifyAttrsRecord;
026import com.mybatisflex.core.mybatis.TypeHandlerObject;
027import com.mybatisflex.core.query.*;
028import com.mybatisflex.core.row.Row;
029import com.mybatisflex.core.tenant.TenantManager;
030import com.mybatisflex.core.util.*;
031import org.apache.ibatis.mapping.ResultFlag;
032import org.apache.ibatis.mapping.ResultMap;
033import org.apache.ibatis.mapping.ResultMapping;
034import org.apache.ibatis.reflection.MetaObject;
035import org.apache.ibatis.reflection.Reflector;
036import org.apache.ibatis.reflection.ReflectorFactory;
037import org.apache.ibatis.session.Configuration;
038import org.apache.ibatis.type.TypeHandler;
039
040import java.util.*;
041
042public class TableInfo {
043
044    private String schema; //schema
045    private String tableName; //表名
046    private Class<?> entityClass; //实体类
047    private boolean camelToUnderline = true;
048    private String dataSource;
049
050    //逻辑删除数据库列名
051    private String logicDeleteColumn;
052
053    //乐观锁字段
054    private String versionColumn;
055
056    //租户ID 字段
057    private String tenantIdColumn;
058
059    //数据插入时,默认插入数据字段
060    private Map<String, String> onInsertColumns;
061
062    //数据更新时,默认更新内容的字段
063    private Map<String, String> onUpdateColumns;
064
065    //大字段列
066    private String[] largeColumns = new String[0];
067
068    // 所有的字段,但除了主键的列
069    private String[] columns = new String[0];
070
071    //主键字段
072    private String[] primaryKeys = new String[0];
073
074    //在插入数据的时候,支持主动插入的主键字段
075    //通过自定义生成器生成 或者 Sequence 在 before 生成的时候,是需要主动插入数据的
076    private String[] insertPrimaryKeys;
077
078    private List<ColumnInfo> columnInfoList;
079    private List<IdInfo> primaryKeyList;
080
081    //column 和 java 属性的称的关系映射
082    private Map<String, ColumnInfo> columnInfoMapping = new HashMap<>();
083    private Map<String, String> propertyColumnMapping = new HashMap<>();
084
085    private InsertListener onInsertListener;
086    private UpdateListener onUpdateListener;
087    private SetListener onSetListener;
088
089
090    private final ReflectorFactory reflectorFactory = new BaseReflectorFactory() {
091        @Override
092        public Reflector findForClass(Class<?> type) {
093            return getReflector();
094        }
095    };
096    private Reflector reflector; //反射工具
097
098    public String getSchema() {
099        return schema;
100    }
101
102    public void setSchema(String schema) {
103        this.schema = schema;
104    }
105
106    public String getTableName() {
107        return tableName;
108    }
109
110    public void setTableName(String tableName) {
111        this.tableName = tableName;
112    }
113
114    public Class<?> getEntityClass() {
115        return entityClass;
116    }
117
118    public void setEntityClass(Class<?> entityClass) {
119        this.entityClass = entityClass;
120    }
121
122    public boolean isCamelToUnderline() {
123        return camelToUnderline;
124    }
125
126    public void setCamelToUnderline(boolean camelToUnderline) {
127        this.camelToUnderline = camelToUnderline;
128    }
129
130    public String getDataSource() {
131        return dataSource;
132    }
133
134    public void setDataSource(String dataSource) {
135        this.dataSource = dataSource;
136    }
137
138    public String getLogicDeleteColumn() {
139        return logicDeleteColumn;
140    }
141
142    public void setLogicDeleteColumn(String logicDeleteColumn) {
143        this.logicDeleteColumn = logicDeleteColumn;
144    }
145
146    public String getVersionColumn() {
147        return versionColumn;
148    }
149
150    public void setVersionColumn(String versionColumn) {
151        this.versionColumn = versionColumn;
152    }
153
154    public String getTenantIdColumn() {
155        return tenantIdColumn;
156    }
157
158    public void setTenantIdColumn(String tenantIdColumn) {
159        this.tenantIdColumn = tenantIdColumn;
160    }
161
162    public Map<String, String> getOnInsertColumns() {
163        return onInsertColumns;
164    }
165
166    public void setOnInsertColumns(Map<String, String> onInsertColumns) {
167        this.onInsertColumns = onInsertColumns;
168    }
169
170    public Map<String, String> getOnUpdateColumns() {
171        return onUpdateColumns;
172    }
173
174    public void setOnUpdateColumns(Map<String, String> onUpdateColumns) {
175        this.onUpdateColumns = onUpdateColumns;
176    }
177
178    public String[] getLargeColumns() {
179        return largeColumns;
180    }
181
182    public void setLargeColumns(String[] largeColumns) {
183        this.largeColumns = largeColumns;
184    }
185
186    public String[] getInsertPrimaryKeys() {
187        return insertPrimaryKeys;
188    }
189
190    public void setInsertPrimaryKeys(String[] insertPrimaryKeys) {
191        this.insertPrimaryKeys = insertPrimaryKeys;
192    }
193
194    public Reflector getReflector() {
195        return reflector;
196    }
197
198    public ReflectorFactory getReflectorFactory() {
199        return reflectorFactory;
200    }
201
202    public void setReflector(Reflector reflector) {
203        this.reflector = reflector;
204    }
205
206    public String[] getColumns() {
207        return columns;
208    }
209
210
211    public void setColumns(String[] columns) {
212        this.columns = columns;
213    }
214
215    public String[] getPrimaryKeys() {
216        return primaryKeys;
217    }
218
219    public void setPrimaryKeys(String[] primaryKeys) {
220        this.primaryKeys = primaryKeys;
221    }
222
223
224    public InsertListener getOnInsertListener() {
225        return onInsertListener;
226    }
227
228    public void setOnInsertListener(InsertListener onInsertListener) {
229        this.onInsertListener = onInsertListener;
230    }
231
232    public UpdateListener getOnUpdateListener() {
233        return onUpdateListener;
234    }
235
236    public void setOnUpdateListener(UpdateListener onUpdateListener) {
237        this.onUpdateListener = onUpdateListener;
238    }
239
240    public SetListener getOnSetListener() {
241        return onSetListener;
242    }
243
244    public void setOnSetListener(SetListener onSetListener) {
245        this.onSetListener = onSetListener;
246    }
247
248    public List<ColumnInfo> getColumnInfoList() {
249        return columnInfoList;
250    }
251
252
253    void setColumnInfoList(List<ColumnInfo> columnInfoList) {
254        this.columnInfoList = columnInfoList;
255        this.columns = new String[columnInfoList.size()];
256        for (int i = 0; i < columnInfoList.size(); i++) {
257            ColumnInfo columnInfo = columnInfoList.get(i);
258            columns[i] = columnInfo.getColumn();
259            columnInfoMapping.put(columnInfo.column, columnInfo);
260            propertyColumnMapping.put(columnInfo.property, columnInfo.column);
261        }
262    }
263
264
265    public List<IdInfo> getPrimaryKeyList() {
266        return primaryKeyList;
267    }
268
269    void setPrimaryKeyList(List<IdInfo> primaryKeyList) {
270        this.primaryKeyList = primaryKeyList;
271        this.primaryKeys = new String[primaryKeyList.size()];
272
273        List<String> insertIdFields = new ArrayList<>();
274        for (int i = 0; i < primaryKeyList.size(); i++) {
275            IdInfo idInfo = primaryKeyList.get(i);
276            primaryKeys[i] = idInfo.getColumn();
277
278            if (idInfo.getKeyType() != KeyType.Auto && (idInfo.getBefore() != null && idInfo.getBefore())) {
279                insertIdFields.add(idInfo.getColumn());
280            }
281
282            columnInfoMapping.put(idInfo.column, idInfo);
283            propertyColumnMapping.put(idInfo.property, idInfo.column);
284        }
285        this.insertPrimaryKeys = insertIdFields.toArray(new String[0]);
286    }
287
288
289    /**
290     * 插入(新增)数据时,获取所有要插入的字段
291     *
292     * @return 字段列表
293     */
294    public String[] obtainInsertColumns() {
295        return ArrayUtil.concat(insertPrimaryKeys, columns);
296    }
297
298
299    /**
300     * 构建 insert 的 Sql 参数
301     *
302     * @param entity 从 entity 中获取
303     * @return 数组
304     */
305    public Object[] buildInsertSqlArgs(Object entity) {
306        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
307        String[] insertColumns = obtainInsertColumns();
308
309        List<Object> values = new ArrayList<>(insertColumns.length);
310        for (String insertColumn : insertColumns) {
311            if (onInsertColumns == null || !onInsertColumns.containsKey(insertColumn)) {
312                Object value = buildColumnSqlArg(metaObject, insertColumn);
313                values.add(value);
314            }
315        }
316        return values.toArray();
317    }
318
319
320    /**
321     * 获取要修改的值
322     *
323     * @param entity
324     * @param ignoreNulls
325     */
326    public Set<String> obtainUpdateColumns(Object entity, boolean ignoreNulls, boolean includePrimary) {
327        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
328        Set<String> columns = new LinkedHashSet<>(); //需使用 LinkedHashSet 保证 columns 的顺序
329        if (entity instanceof ModifyAttrsRecord) {
330            Set<String> properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs();
331            if (properties.isEmpty()) {
332                return Collections.emptySet();
333            }
334            for (String property : properties) {
335                String column = propertyColumnMapping.get(property);
336                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
337                    continue;
338                }
339
340                //过滤乐观锁字段 和 租户字段
341                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
342                    continue;
343                }
344
345                if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) {
346                    continue;
347                }
348
349                // ModifyAttrsRecord 忽略 ignoreNulls 的设置
350                // Object value = getPropertyValue(metaObject, property);
351                // if (ignoreNulls && value == null) {
352                //     continue;
353                // }
354                columns.add(column);
355            }
356        }
357        //not ModifyAttrsRecord
358        else {
359            for (String column : this.columns) {
360                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
361                    continue;
362                }
363
364                //过滤乐观锁字段 和 租户字段
365                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
366                    continue;
367                }
368
369                Object value = buildColumnSqlArg(metaObject, column);
370                if (ignoreNulls && value == null) {
371                    continue;
372                }
373
374                columns.add(column);
375            }
376
377            // 普通 entity(非 ModifyAttrsRecord) 忽略 includePrimary 的设置
378//            if (includePrimary) {
379//                for (String column : this.primaryKeys) {
380//                    Object value = getColumnValue(metaObject, column);
381//                    if (ignoreNulls && value == null) {
382//                        continue;
383//                    }
384//                    columns.add(column);
385//                }
386//            }
387        }
388        return columns;
389    }
390
391    /**
392     * 获取所有要修改的值,默认为全部除了主键以外的字段
393     *
394     * @param entity 实体对象
395     * @return 数组
396     */
397    public Object[] buildUpdateSqlArgs(Object entity, boolean ignoreNulls, boolean includePrimary) {
398        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
399        List<Object> values = new ArrayList<>();
400        if (entity instanceof ModifyAttrsRecord) {
401            Set<String> properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs();
402            if (properties.isEmpty()) {
403                return values.toArray();
404            }
405            for (String property : properties) {
406                String column = propertyColumnMapping.get(property);
407                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
408                    continue;
409                }
410                //过滤乐观锁字段 和 租户字段
411                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
412                    continue;
413                }
414
415                if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) {
416                    continue;
417                }
418
419                Object value = getPropertyValue(metaObject, property);
420
421                // ModifyAttrsRecord 忽略 ignoreNulls 的设置,
422                // 当使用 ModifyAttrsRecord 时,可以理解为要对字段进行 null 值进行更新,否则没必要使用 ModifyAttrsRecord
423                // if (ignoreNulls && value == null) {
424                //    continue;
425                // }
426                values.add(value);
427            }
428        }
429        // normal entity. not ModifyAttrsRecord
430        else {
431            for (String column : this.columns) {
432                if (onUpdateColumns != null && onUpdateColumns.containsKey(column)) {
433                    continue;
434                }
435
436                //过滤乐观锁字段 和 租户字段
437                if (ObjectUtil.equalsAny(column, versionColumn, tenantIdColumn)) {
438                    continue;
439                }
440
441                // 普通 entity 忽略 includePrimary 的设置,
442                // 因为 for 循环中的 this.columns 本身就不包含有主键
443                // if (includePrimary) {
444                // }
445
446                Object value = buildColumnSqlArg(metaObject, column);
447                if (ignoreNulls && value == null) {
448                    continue;
449                }
450
451                values.add(value);
452            }
453        }
454
455        return values.toArray();
456    }
457
458
459    /**
460     * 构建主键的 sql 参数数据
461     *
462     * @param entity
463     */
464    public Object[] buildPkSqlArgs(Object entity) {
465        MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
466        Object[] values = new Object[primaryKeys.length];
467        for (int i = 0; i < primaryKeys.length; i++) {
468            values[i] = buildColumnSqlArg(metaObject, primaryKeys[i]);
469        }
470        return values;
471    }
472
473
474    public Object[] buildTenantIdArgs() {
475        if (StringUtil.isBlank(tenantIdColumn)) {
476            return null;
477        }
478
479        return TenantManager.getTenantIds();
480    }
481
482    private static final String APPEND_CONDITIONS_FLAG = "appendConditions";
483
484    public void appendConditions(Object entity, QueryWrapper queryWrapper) {
485
486        Object appendConditions = CPI.getContext(queryWrapper, APPEND_CONDITIONS_FLAG);
487        if (Boolean.TRUE.equals(appendConditions)) {
488            return;
489        } else {
490            CPI.putContext(queryWrapper, APPEND_CONDITIONS_FLAG, Boolean.TRUE);
491        }
492
493        //添加乐观锁条件,只有在 update 的时候进行处理
494        if (StringUtil.isNotBlank(versionColumn) && entity != null) {
495            Object versionValue = buildColumnSqlArg(entity, versionColumn);
496            if (versionValue == null) {
497                throw FlexExceptions.wrap("The version value of entity[%s] must not be null.", entity);
498            }
499            queryWrapper.and(QueryCondition.create(tableName, versionColumn, QueryCondition.LOGIC_EQUALS, versionValue));
500        }
501
502        //逻辑删除条件,已删除的数据不能被修改
503        if (StringUtil.isNotBlank(logicDeleteColumn)) {
504            queryWrapper.and(QueryCondition.create(tableName, logicDeleteColumn, QueryCondition.LOGIC_EQUALS
505                    , FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete()));
506        }
507
508        //多租户
509        Object[] tenantIdArgs = buildTenantIdArgs();
510        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
511            if (tenantIdArgs.length == 1) {
512                queryWrapper.and(QueryCondition.create(tableName, tenantIdColumn, QueryCondition.LOGIC_EQUALS, tenantIdArgs[0]));
513            } else {
514                queryWrapper.and(QueryCondition.create(tableName, tenantIdColumn, QueryCondition.LOGIC_IN, tenantIdArgs));
515            }
516        }
517
518        //子查询
519        List<QueryWrapper> childSelects = CPI.getChildSelect(queryWrapper);
520        if (CollectionUtil.isNotEmpty(childSelects)) {
521            for (QueryWrapper childQueryWrapper : childSelects) {
522                List<QueryTable> queryTables = CPI.getQueryTables(childQueryWrapper);
523                for (QueryTable queryTable : queryTables) {
524                    TableInfo tableInfo = TableInfoFactory.ofTableName(queryTable.getName());
525                    if (tableInfo != null) {
526                        tableInfo.appendConditions(entity, childQueryWrapper);
527                    }
528                }
529            }
530        }
531
532        //union
533        List<UnionWrapper> unions = CPI.getUnions(queryWrapper);
534        if (CollectionUtil.isNotEmpty(unions)) {
535            for (UnionWrapper union : unions) {
536                QueryWrapper unionQueryWrapper = union.getQueryWrapper();
537                List<QueryTable> queryTables = CPI.getQueryTables(unionQueryWrapper);
538                for (QueryTable queryTable : queryTables) {
539                    TableInfo tableInfo = TableInfoFactory.ofTableName(queryTable.getName());
540                    if (tableInfo != null) {
541                        tableInfo.appendConditions(entity, unionQueryWrapper);
542                    }
543                }
544            }
545        }
546
547    }
548
549
550    public String getKeyProperties() {
551        StringJoiner joiner = new StringJoiner(",");
552        for (IdInfo value : primaryKeyList) {
553            joiner.add(FlexConsts.ENTITY + "." + value.getProperty());
554        }
555        return joiner.toString();
556    }
557
558
559    public String getKeyColumns() {
560        StringJoiner joiner = new StringJoiner(",");
561        for (IdInfo value : primaryKeyList) {
562            joiner.add(value.getColumn());
563        }
564        return joiner.toString();
565    }
566
567    public ResultMap buildResultMap(Configuration configuration) {
568        String resultMapId = entityClass.getName();
569        List<ResultMapping> resultMappings = new ArrayList<>();
570
571        for (ColumnInfo columnInfo : columnInfoList) {
572            ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.getProperty(),
573                    columnInfo.getColumn(), columnInfo.getPropertyType())
574                    .jdbcType(columnInfo.getJdbcType())
575                    .typeHandler(columnInfo.buildTypeHandler())
576                    .build();
577            resultMappings.add(mapping);
578        }
579
580        for (IdInfo idInfo : primaryKeyList) {
581            ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.getProperty(),
582                    idInfo.getColumn(), idInfo.getPropertyType())
583                    .flags(CollectionUtil.newArrayList(ResultFlag.ID))
584                    .jdbcType(idInfo.getJdbcType())
585                    .typeHandler(idInfo.buildTypeHandler())
586                    .build();
587            resultMappings.add(mapping);
588        }
589
590        return new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
591    }
592
593
594    private Object buildColumnSqlArg(MetaObject metaObject, String column) {
595        ColumnInfo columnInfo = columnInfoMapping.get(column);
596        Object value = getPropertyValue(metaObject, columnInfo.property);
597
598        TypeHandler typeHandler = columnInfo.buildTypeHandler();
599        if (value != null && typeHandler != null) {
600            return new TypeHandlerObject(typeHandler, value, columnInfo.getJdbcType());
601        }
602
603        return value;
604    }
605
606
607    public Object buildColumnSqlArg(Object entityObject, String column) {
608        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
609        return buildColumnSqlArg(metaObject, column);
610    }
611
612
613    private Object getPropertyValue(MetaObject metaObject, String property) {
614        if (property != null && metaObject.hasGetter(property)) {
615            return metaObject.getValue(property);
616        }
617        return null;
618    }
619
620
621    /**
622     * 通过 row 实例类转换为一个 entity
623     *
624     * @return entity
625     */
626    public <T> T newInstanceByRow(Row row) {
627        Object instance = ClassUtil.newInstance(entityClass);
628        MetaObject metaObject = EntityMetaObject.forObject(instance, reflectorFactory);
629        for (String column : row.keySet()) {
630            ColumnInfo columnInfo = columnInfoMapping.get(column);
631            if (columnInfo != null && metaObject.hasSetter(columnInfo.property)) {
632                Object value = ConvertUtil.convert(row.get(column), metaObject.getSetterType(columnInfo.property));
633                if (onSetListener != null) {
634                    value = onSetListener.onSet(instance, columnInfo.property, value);
635                }
636                metaObject.setValue(columnInfo.property, value);
637            }
638        }
639        return (T) instance;
640    }
641
642
643    /**
644     * 初始化乐观锁版本号
645     *
646     * @param entityObject
647     */
648    public void initVersionValueIfNecessary(Object entityObject) {
649        if (StringUtil.isBlank(versionColumn)) {
650            return;
651        }
652
653        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
654        Object columnValue = getPropertyValue(metaObject, columnInfoMapping.get(versionColumn).property);
655        if (columnValue == null) {
656            String name = columnInfoMapping.get(versionColumn).property;
657            Class<?> clazz = metaObject.getSetterType(name);
658            metaObject.setValue(name, ConvertUtil.convert(0L, clazz));
659        }
660    }
661
662    /**
663     * 设置租户id
664     *
665     * @param entityObject
666     */
667    public void initTenantIdIfNecessary(Object entityObject) {
668        if (StringUtil.isBlank(tenantIdColumn)) {
669            return;
670        }
671
672        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
673        Object[] tenantIds = TenantManager.getTenantIds();
674        if (tenantIds == null || tenantIds.length == 0) {
675            return;
676        }
677
678        //默认使用第一个作为插入的租户ID
679        Object tenantId = tenantIds[0];
680        if (tenantId != null) {
681            metaObject.setValue(columnInfoMapping.get(tenantIdColumn).property, tenantId);
682        }
683    }
684
685    /**
686     * 初始化逻辑删除的默认值
687     *
688     * @param entityObject
689     */
690    public void initLogicDeleteValueIfNecessary(Object entityObject) {
691        if (StringUtil.isBlank(logicDeleteColumn)) {
692            return;
693        }
694
695        MetaObject metaObject = EntityMetaObject.forObject(entityObject, reflectorFactory);
696        Object columnValue = getPropertyValue(metaObject, columnInfoMapping.get(logicDeleteColumn).property);
697        if (columnValue == null) {
698            String name = columnInfoMapping.get(logicDeleteColumn).property;
699            Class<?> clazz = metaObject.getSetterType(name);
700            if (Number.class.isAssignableFrom(clazz)){
701                metaObject.setValue(name, ConvertUtil.convert(0L, clazz));
702            }else if (clazz == Boolean.class){
703                metaObject.setValue(name, false);
704            }
705        }
706    }
707
708
709    public void invokeOnInsertListener(Object entity) {
710        if (onInsertListener != null) {
711            onInsertListener.onInsert(entity);
712            return;
713        }
714
715        InsertListener globalInsertListener = FlexGlobalConfig.getDefaultConfig().getInsertListener(entityClass);
716        if (globalInsertListener != null) {
717            globalInsertListener.onInsert(entity);
718        }
719    }
720
721
722    public void invokeOnUpdateListener(Object entity) {
723        if (onUpdateListener != null) {
724            onUpdateListener.onUpdate(entity);
725            return;
726        }
727
728        UpdateListener globalUpdateListener = FlexGlobalConfig.getDefaultConfig().getUpdateListener(entityClass);
729        if (globalUpdateListener != null) {
730            globalUpdateListener.onUpdate(entity);
731        }
732    }
733
734
735    public Object invokeOnSetListener(Object entity, String property, Object value) {
736        if (onSetListener != null) {
737            return onSetListener.onSet(entity, property, value);
738        }
739
740        SetListener globalSetListener = FlexGlobalConfig.getDefaultConfig().getSetListener(entityClass);
741        if (globalSetListener != null) {
742            return globalSetListener.onSet(entity, property, value);
743        }
744
745        return value;
746    }
747}