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