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