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 //以使用者注解配置为主 090 this.targetTableInfo = StringUtil.isBlank(targetTable) ? TableInfoFactory.ofEntityClass(relationFieldWrapper.getMappingType()) : TableInfoFactory.ofTableName(targetTable); 091 this.targetSchema = targetTableInfo != null ? targetTableInfo.getSchema() : targetSchema; 092 this.targetTable = targetTableInfo != null ? targetTableInfo.getTableName() : targetTable; 093 094 //当指定了 valueField 的时候,一般是 String Integer 等基本数据类型 095 this.targetEntityClass = (StringUtil.isNotBlank(valueField) && targetTableInfo != null) ? targetTableInfo.getEntityClass() : relationFieldWrapper.getMappingType(); 096 097 this.targetField = ClassUtil.getFirstField(targetEntityClass, field -> field.getName().equalsIgnoreCase(targetField)); 098 if (this.targetField == null) { 099 throw new IllegalStateException("Can not find field by name \"" + targetField + "\" from class: " + targetEntityClass.getName()); 100 } 101 102 this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField); 103 104 this.valueField = valueField; 105 this.onlyQueryValueField = StringUtil.isNotBlank(valueField); 106 107 this.conditionColumn = targetTableInfo == null ? column(targetTable, StringUtil.camelToUnderline(this.targetField.getName())) 108 : column(targetTable, targetTableInfo.getColumnByProperty(this.targetField.getName())); 109 110 if (onlyQueryValueField) { 111 //仅绑定字段时只需要查询关联列和该字段列即可 112 this.selectColumns = new String[]{conditionColumn.getName(), targetTableInfo != null ? targetTableInfo.getColumnByProperty(this.valueField) : StringUtil.camelToUnderline(this.valueField)}; 113 } else { 114 if (ArrayUtil.isNotEmpty(selectColumns)) { 115 if (ArrayUtil.contains(selectColumns, conditionColumn.getName())) { 116 this.selectColumns = selectColumns; 117 } else { 118 //需要追加 conditionColumn,因为进行内存 join 的时候,需要用到这个内容进行对比 119 this.selectColumns = ArrayUtil.concat(selectColumns, new String[]{conditionColumn.getName()}); 120 } 121 } 122 123 } 124 125 initExtraCondition(extraCondition); 126 } 127 128 protected void initExtraCondition(String extraCondition) { 129 if (StringUtil.isBlank(extraCondition)) { 130 return; 131 } 132 133 134 List<String> sqlParamKeys = null; 135 char[] chars = extraCondition.toCharArray(); 136 StringBuilder sqlBuilder = new StringBuilder(); 137 sqlBuilder.append(chars[0]); 138 char prev, current; 139 boolean keyStart = false; 140 StringBuilder currentKey = null; 141 for (int i = 1; i < chars.length; i++) { 142 prev = chars[i - 1]; 143 current = chars[i]; 144 if (prev == ' ' && current == ':') { 145 keyStart = true; 146 currentKey = new StringBuilder(); 147 } else if (keyStart) { 148 if (current != ' ' && current != ')') { 149 currentKey.append(current); 150 } else { 151 if (sqlParamKeys == null) { 152 sqlParamKeys = new ArrayList<>(); 153 } 154 sqlParamKeys.add(currentKey.toString()); 155 sqlBuilder.append("?").append(current); 156 keyStart = false; 157 currentKey = null; 158 } 159 } else { 160 sqlBuilder.append(current); 161 } 162 } 163 if (keyStart && currentKey != null && currentKey.length() > 0) { 164 if (sqlParamKeys == null) { 165 sqlParamKeys = new ArrayList<>(); 166 } 167 sqlParamKeys.add(currentKey.toString()); 168 sqlBuilder.append(" ?"); 169 } 170 171 this.extraConditionSql = sqlBuilder.toString(); 172 this.extraConditionParamKeys = sqlParamKeys != null ? sqlParamKeys : Collections.emptyList(); 173 } 174 175 public String getName() { 176 return name; 177 } 178 179 public String getSimpleName() { 180 return simpleName; 181 } 182 183 public Class<SelfEntity> getSelfEntityClass() { 184 return selfEntityClass; 185 } 186 187 public void setSelfEntityClass(Class<SelfEntity> selfEntityClass) { 188 this.selfEntityClass = selfEntityClass; 189 } 190 191 public Field getRelationField() { 192 return relationField; 193 } 194 195 public void setRelationField(Field relationField) { 196 this.relationField = relationField; 197 } 198 199 public FieldWrapper getRelationFieldWrapper() { 200 return relationFieldWrapper; 201 } 202 203 public void setRelationFieldWrapper(FieldWrapper relationFieldWrapper) { 204 this.relationFieldWrapper = relationFieldWrapper; 205 } 206 207 public Field getSelfField() { 208 return selfField; 209 } 210 211 public void setSelfField(Field selfField) { 212 this.selfField = selfField; 213 } 214 215 public FieldWrapper getSelfFieldWrapper() { 216 return selfFieldWrapper; 217 } 218 219 public void setSelfFieldWrapper(FieldWrapper selfFieldWrapper) { 220 this.selfFieldWrapper = selfFieldWrapper; 221 } 222 223 public Field getTargetField() { 224 return targetField; 225 } 226 227 public void setTargetField(Field targetField) { 228 this.targetField = targetField; 229 } 230 231 public Class<?> getTargetEntityClass() { 232 return targetEntityClass; 233 } 234 235 public void setTargetEntityClass(Class<?> targetEntityClass) { 236 this.targetEntityClass = targetEntityClass; 237 } 238 239 public TableInfo getTargetTableInfo() { 240 return targetTableInfo; 241 } 242 243 public void setTargetTableInfo(TableInfo targetTableInfo) { 244 this.targetTableInfo = targetTableInfo; 245 } 246 247 public FieldWrapper getTargetFieldWrapper() { 248 return targetFieldWrapper; 249 } 250 251 public void setTargetFieldWrapper(FieldWrapper targetFieldWrapper) { 252 this.targetFieldWrapper = targetFieldWrapper; 253 } 254 255 public String getTargetSchema() { 256 return targetSchema; 257 } 258 259 public void setTargetSchema(String targetSchema) { 260 this.targetSchema = targetSchema; 261 } 262 263 public String getTargetTable() { 264 return targetTable; 265 } 266 267 public void setTargetTable(String targetTable) { 268 this.targetTable = targetTable; 269 } 270 271 public String getValueField() { 272 return valueField; 273 } 274 275 public void setValueField(String valueField) { 276 this.valueField = valueField; 277 } 278 279 public boolean isOnlyQueryValueField() { 280 return onlyQueryValueField; 281 } 282 283 public void setOnlyQueryValueField(boolean onlyQueryValueField) { 284 this.onlyQueryValueField = onlyQueryValueField; 285 } 286 287 public String getJoinTable() { 288 return joinTable; 289 } 290 291 public void setJoinTable(String joinTable) { 292 this.joinTable = joinTable; 293 } 294 295 public String getJoinSelfColumn() { 296 return joinSelfColumn; 297 } 298 299 public void setJoinSelfColumn(String joinSelfColumn) { 300 this.joinSelfColumn = joinSelfColumn; 301 } 302 303 public String getJoinTargetColumn() { 304 return joinTargetColumn; 305 } 306 307 public void setJoinTargetColumn(String joinTargetColumn) { 308 this.joinTargetColumn = joinTargetColumn; 309 } 310 311 public Set<Object> getSelfFieldValues(List<SelfEntity> selfEntities) { 312 if (selfEntities == null || selfEntities.isEmpty()) { 313 return Collections.emptySet(); 314 } 315 Set<Object> values = new LinkedHashSet<>(); 316 selfEntities.forEach(self -> { 317 Object value = selfFieldWrapper.get(self); 318 if (value != null && !"".equals(value)) { 319 values.add(value); 320 } 321 }); 322 return values; 323 } 324 325 326 public Class<?> getMappingType() { 327 return relationFieldWrapper.getMappingType(); 328 } 329 330 public String getDataSource() { 331 return dataSource; 332 } 333 334 public void setDataSource(String dataSource) { 335 this.dataSource = dataSource; 336 } 337 338 public String getTargetTableWithSchema() { 339 if (StringUtil.isNotBlank(targetTable)) { 340 return StringUtil.isNotBlank(targetSchema) ? targetSchema + "." + targetTable : targetTable; 341 } else { 342 return targetTableInfo.getTableNameWithSchema(); 343 } 344 } 345 346 protected boolean isRelationByMiddleTable() { 347 return StringUtil.isNotBlank(joinTable); 348 } 349 350 351 protected static Class<?> getTargetEntityClass(Class<?> entityClass, Field relationField) { 352 return FieldWrapper.of(entityClass, relationField.getName()).getMappingType(); 353 } 354 355 protected static String getDefaultPrimaryProperty(String key, Class<?> entityClass, String message) { 356 if (StringUtil.isNotBlank(key)) { 357 return key; 358 } 359 360 TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass); 361 List<IdInfo> primaryKeyList = tableInfo.getPrimaryKeyList(); 362 if (primaryKeyList == null || primaryKeyList.size() != 1) { 363 throw FlexExceptions.wrap(message); 364 } 365 366 return primaryKeyList.get(0).getProperty(); 367 } 368 369 370 /** 371 * 构建查询目标对象的 QueryWrapper 372 * 373 * @param targetValues 条件的值 374 * @return QueryWrapper 375 */ 376 public QueryWrapper buildQueryWrapper(Set<Object> targetValues) { 377 QueryWrapper queryWrapper = QueryWrapper.create(); 378 379 if (ArrayUtil.isNotEmpty(selectColumns)) { 380 queryWrapper.select(selectColumns); 381 } 382 383 queryWrapper.from(getTargetTableWithSchema()); 384 385 if (targetValues.size() > 1) { 386 queryWrapper.where(conditionColumn.in(targetValues)); 387 } else { 388 queryWrapper.where(conditionColumn.eq(targetValues.iterator().next())); 389 } 390 391 if (StringUtil.isNotBlank(extraConditionSql)) { 392 queryWrapper.and(extraConditionSql, RelationManager.getExtraConditionParams(extraConditionParamKeys)); 393 } 394 395 customizeQueryWrapper(queryWrapper); 396 397 return queryWrapper; 398 } 399 400 401 /** 402 * 方便子类追加自定义的条件 403 * 404 * @param queryWrapper 查询条件 405 */ 406 public void customizeQueryWrapper(QueryWrapper queryWrapper) { 407 //do thing 408 } 409 410 411 /** 412 * @param selfEntities 当前的实体类列表 413 * @param targetObjectList 查询到的结果 414 * @param mappingRows 中间表的映射数据,非中间表查询的场景下,mappingRows 永远为 null 415 */ 416 public abstract void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows); 417 418}