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