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}