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