001/* 002 * Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.relation; 017 018import com.mybatisflex.core.exception.FlexExceptions; 019import com.mybatisflex.core.query.QueryColumn; 020import com.mybatisflex.core.query.QueryWrapper; 021import com.mybatisflex.core.row.Row; 022import com.mybatisflex.core.table.IdInfo; 023import com.mybatisflex.core.table.TableInfo; 024import com.mybatisflex.core.table.TableInfoFactory; 025import com.mybatisflex.core.util.ArrayUtil; 026import com.mybatisflex.core.util.ClassUtil; 027import com.mybatisflex.core.util.FieldWrapper; 028import com.mybatisflex.core.util.StringUtil; 029 030import java.lang.reflect.Field; 031import java.util.*; 032 033import static com.mybatisflex.core.query.QueryMethods.column; 034 035public abstract class AbstractRelation<SelfEntity> { 036 037 protected String name; 038 protected String simpleName; 039 protected Class<SelfEntity> selfEntityClass; 040 protected Field relationField; 041 protected FieldWrapper relationFieldWrapper; 042 043 protected Field selfField; 044 protected FieldWrapper selfFieldWrapper; 045 046 protected String targetSchema; 047 protected String targetTable; 048 protected Field targetField; 049 050 protected String valueField; 051 protected boolean onlyQueryValueField; 052 053 protected Class<?> targetEntityClass; 054 protected TableInfo targetTableInfo; 055 protected FieldWrapper targetFieldWrapper; 056 057 protected String joinTable; 058 protected String joinSelfColumn; 059 protected String joinTargetColumn; 060 061 protected String dataSource; 062 063 protected String extraConditionSql; 064 protected List<String> extraConditionParamKeys; 065 066 protected QueryColumn conditionColumn; 067 protected String[] selectColumns; 068 069 public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField, String valueField, 070 String joinTable, String joinSelfColumn, String joinTargetColumn, 071 String dataSource, Class<SelfEntity> entityClass, Field relationField, 072 String extraCondition, String[] selectColumns 073 ) { 074 this.name = entityClass.getSimpleName() + "." + relationField.getName(); 075 this.simpleName = relationField.getName(); 076 this.selfEntityClass = entityClass; 077 this.relationField = relationField; 078 this.relationFieldWrapper = FieldWrapper.of(entityClass, relationField.getName()); 079 080 this.joinTable = joinTable; 081 this.joinSelfColumn = joinSelfColumn; 082 this.joinTargetColumn = joinTargetColumn; 083 084 this.dataSource = dataSource; 085 086 this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equalsIgnoreCase(selfField)); 087 this.selfFieldWrapper = FieldWrapper.of(entityClass, selfField); 088 089 String tableNameWithSchema = StringUtil.buildSchemaWithTable(targetSchema, targetTable); 090 //以使用者注解配置为主 091 this.targetTableInfo = StringUtil.noText(targetTable) ? TableInfoFactory.ofEntityClass(relationFieldWrapper.getMappingType()) : TableInfoFactory.ofTableName(tableNameWithSchema); 092 this.targetSchema = targetTableInfo != null ? targetTableInfo.getSchema() : targetSchema; 093 this.targetTable = targetTableInfo != null ? targetTableInfo.getTableName() : targetTable; 094 095 //当指定了 valueField 的时候,一般是 String Integer 等基本数据类型 096 this.targetEntityClass = (StringUtil.hasText(valueField) && targetTableInfo != null) ? targetTableInfo.getEntityClass() : relationFieldWrapper.getMappingType(); 097 098 this.targetField = ClassUtil.getFirstField(targetEntityClass, field -> field.getName().equalsIgnoreCase(targetField)); 099 if (this.targetField == null) { 100 throw new IllegalStateException("Can not find field by name \"" + targetField + "\" from class: " + targetEntityClass.getName()); 101 } 102 103 this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField); 104 105 this.valueField = valueField; 106 this.onlyQueryValueField = StringUtil.hasText(valueField); 107 108 this.conditionColumn = targetTableInfo == null ? column(targetTable, StringUtil.camelToUnderline(this.targetField.getName())) 109 : column(targetTable, targetTableInfo.getColumnByProperty(this.targetField.getName())); 110 111 if (onlyQueryValueField) { 112 //仅绑定字段时只需要查询关联列和该字段列即可 113 this.selectColumns = new String[]{conditionColumn.getName(), targetTableInfo != null ? targetTableInfo.getColumnByProperty(this.valueField) : StringUtil.camelToUnderline(this.valueField)}; 114 } else { 115 if (ArrayUtil.isNotEmpty(selectColumns)) { 116 if (ArrayUtil.contains(selectColumns, conditionColumn.getName())) { 117 this.selectColumns = selectColumns; 118 } else { 119 //需要追加 conditionColumn,因为进行内存 join 的时候,需要用到这个内容进行对比 120 this.selectColumns = ArrayUtil.concat(selectColumns, new String[]{conditionColumn.getName()}); 121 } 122 } 123 124 } 125 126 initExtraCondition(extraCondition); 127 } 128 129 protected void initExtraCondition(String extraCondition) { 130 if (StringUtil.noText(extraCondition)) { 131 return; 132 } 133 134 135 List<String> sqlParamKeys = null; 136 char[] chars = extraCondition.toCharArray(); 137 StringBuilder sqlBuilder = new StringBuilder(); 138 sqlBuilder.append(chars[0]); 139 char prev, current; 140 boolean keyStart = false; 141 StringBuilder currentKey = null; 142 for (int i = 1; i < chars.length; i++) { 143 prev = chars[i - 1]; 144 current = chars[i]; 145 if (prev == ' ' && current == ':') { 146 keyStart = true; 147 currentKey = new StringBuilder(); 148 } else if (keyStart) { 149 if (current != ' ' && current != ')') { 150 currentKey.append(current); 151 } else { 152 if (sqlParamKeys == null) { 153 sqlParamKeys = new ArrayList<>(); 154 } 155 sqlParamKeys.add(currentKey.toString()); 156 sqlBuilder.append("?").append(current); 157 keyStart = false; 158 currentKey = null; 159 } 160 } else { 161 sqlBuilder.append(current); 162 } 163 } 164 if (keyStart && currentKey != null && currentKey.length() > 0) { 165 if (sqlParamKeys == null) { 166 sqlParamKeys = new ArrayList<>(); 167 } 168 sqlParamKeys.add(currentKey.toString()); 169 sqlBuilder.append(" ?"); 170 } 171 172 this.extraConditionSql = sqlBuilder.toString(); 173 this.extraConditionParamKeys = sqlParamKeys != null ? sqlParamKeys : Collections.emptyList(); 174 } 175 176 public String getName() { 177 return name; 178 } 179 180 public String getSimpleName() { 181 return simpleName; 182 } 183 184 public Class<SelfEntity> getSelfEntityClass() { 185 return selfEntityClass; 186 } 187 188 public void setSelfEntityClass(Class<SelfEntity> selfEntityClass) { 189 this.selfEntityClass = selfEntityClass; 190 } 191 192 public Field getRelationField() { 193 return relationField; 194 } 195 196 public void setRelationField(Field relationField) { 197 this.relationField = relationField; 198 } 199 200 public FieldWrapper getRelationFieldWrapper() { 201 return relationFieldWrapper; 202 } 203 204 public void setRelationFieldWrapper(FieldWrapper relationFieldWrapper) { 205 this.relationFieldWrapper = relationFieldWrapper; 206 } 207 208 public Field getSelfField() { 209 return selfField; 210 } 211 212 public void setSelfField(Field selfField) { 213 this.selfField = selfField; 214 } 215 216 public FieldWrapper getSelfFieldWrapper() { 217 return selfFieldWrapper; 218 } 219 220 public void setSelfFieldWrapper(FieldWrapper selfFieldWrapper) { 221 this.selfFieldWrapper = selfFieldWrapper; 222 } 223 224 public Field getTargetField() { 225 return targetField; 226 } 227 228 public void setTargetField(Field targetField) { 229 this.targetField = targetField; 230 } 231 232 public Class<?> getTargetEntityClass() { 233 return targetEntityClass; 234 } 235 236 public void setTargetEntityClass(Class<?> targetEntityClass) { 237 this.targetEntityClass = targetEntityClass; 238 } 239 240 public TableInfo getTargetTableInfo() { 241 return targetTableInfo; 242 } 243 244 public void setTargetTableInfo(TableInfo targetTableInfo) { 245 this.targetTableInfo = targetTableInfo; 246 } 247 248 public FieldWrapper getTargetFieldWrapper() { 249 return targetFieldWrapper; 250 } 251 252 public void setTargetFieldWrapper(FieldWrapper targetFieldWrapper) { 253 this.targetFieldWrapper = targetFieldWrapper; 254 } 255 256 public String getTargetSchema() { 257 return targetSchema; 258 } 259 260 public void setTargetSchema(String targetSchema) { 261 this.targetSchema = targetSchema; 262 } 263 264 public String getTargetTable() { 265 return targetTable; 266 } 267 268 public void setTargetTable(String targetTable) { 269 this.targetTable = targetTable; 270 } 271 272 public String getValueField() { 273 return valueField; 274 } 275 276 public void setValueField(String valueField) { 277 this.valueField = valueField; 278 } 279 280 public boolean isOnlyQueryValueField() { 281 return onlyQueryValueField; 282 } 283 284 public void setOnlyQueryValueField(boolean onlyQueryValueField) { 285 this.onlyQueryValueField = onlyQueryValueField; 286 } 287 288 public String getJoinTable() { 289 return joinTable; 290 } 291 292 public void setJoinTable(String joinTable) { 293 this.joinTable = joinTable; 294 } 295 296 public String getJoinSelfColumn() { 297 return joinSelfColumn; 298 } 299 300 public void setJoinSelfColumn(String joinSelfColumn) { 301 this.joinSelfColumn = joinSelfColumn; 302 } 303 304 public String getJoinTargetColumn() { 305 return joinTargetColumn; 306 } 307 308 public void setJoinTargetColumn(String joinTargetColumn) { 309 this.joinTargetColumn = joinTargetColumn; 310 } 311 312 public Set<Object> getSelfFieldValues(List<SelfEntity> selfEntities) { 313 if (selfEntities == null || selfEntities.isEmpty()) { 314 return Collections.emptySet(); 315 } 316 Set<Object> values = new LinkedHashSet<>(); 317 selfEntities.forEach(self -> { 318 Object value = selfFieldWrapper.get(self); 319 if (value != null && !"".equals(value)) { 320 values.add(value); 321 } 322 }); 323 return values; 324 } 325 326 327 public Class<?> getMappingType() { 328 return relationFieldWrapper.getMappingType(); 329 } 330 331 public String getDataSource() { 332 return dataSource; 333 } 334 335 public void setDataSource(String dataSource) { 336 this.dataSource = dataSource; 337 } 338 339 public String getTargetTableWithSchema() { 340 if (StringUtil.hasText(targetTable)) { 341 return StringUtil.hasText(targetSchema) ? targetSchema + "." + targetTable : targetTable; 342 } else { 343 return targetTableInfo.getTableNameWithSchema(); 344 } 345 } 346 347 protected boolean isRelationByMiddleTable() { 348 return StringUtil.hasText(joinTable); 349 } 350 351 352 protected static Class<?> getTargetEntityClass(Class<?> entityClass, Field relationField) { 353 return FieldWrapper.of(entityClass, relationField.getName()).getMappingType(); 354 } 355 356 protected static String getDefaultPrimaryProperty(String key, Class<?> entityClass, String message) { 357 if (StringUtil.hasText(key)) { 358 return key; 359 } 360 361 TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass); 362 List<IdInfo> primaryKeyList = tableInfo.getPrimaryKeyList(); 363 if (primaryKeyList == null || primaryKeyList.size() != 1) { 364 throw FlexExceptions.wrap(message); 365 } 366 367 return primaryKeyList.get(0).getProperty(); 368 } 369 370 371 /** 372 * 构建查询目标对象的 QueryWrapper 373 * 374 * @param targetValues 条件的值 375 * @return QueryWrapper 376 */ 377 public QueryWrapper buildQueryWrapper(Set<Object> targetValues) { 378 QueryWrapper queryWrapper = QueryWrapper.create(); 379 380 if (ArrayUtil.isNotEmpty(selectColumns)) { 381 queryWrapper.select(selectColumns); 382 } 383 384 queryWrapper.from(getTargetTableWithSchema()); 385 386 if (targetValues.size() > 1) { 387 queryWrapper.where(conditionColumn.in(targetValues)); 388 } else { 389 queryWrapper.where(conditionColumn.eq(targetValues.iterator().next())); 390 } 391 392 if (StringUtil.hasText(extraConditionSql)) { 393 queryWrapper.and(extraConditionSql, RelationManager.getExtraConditionParams(extraConditionParamKeys)); 394 } 395 396 customizeQueryWrapper(queryWrapper); 397 398 return queryWrapper; 399 } 400 401 402 /** 403 * 方便子类追加自定义的条件 404 * 405 * @param queryWrapper 查询条件 406 */ 407 public void customizeQueryWrapper(QueryWrapper queryWrapper) { 408 //do thing 409 } 410 411 412 /** 413 * @param selfEntities 当前的实体类列表 414 * @param targetObjectList 查询到的结果 415 * @param mappingRows 中间表的映射数据,非中间表查询的场景下,mappingRows 永远为 null 416 */ 417 public abstract void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows); 418 419}