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.annotation.RelationManyToMany; 019import com.mybatisflex.annotation.RelationManyToOne; 020import com.mybatisflex.annotation.RelationOneToMany; 021import com.mybatisflex.annotation.RelationOneToOne; 022import com.mybatisflex.core.BaseMapper; 023import com.mybatisflex.core.FlexConsts; 024import com.mybatisflex.core.FlexGlobalConfig; 025import com.mybatisflex.core.datasource.DataSourceKey; 026import com.mybatisflex.core.query.QueryWrapper; 027import com.mybatisflex.core.row.Row; 028import com.mybatisflex.core.util.ClassUtil; 029import com.mybatisflex.core.util.CollectionUtil; 030import com.mybatisflex.core.util.LambdaGetter; 031import com.mybatisflex.core.util.LambdaUtil; 032import com.mybatisflex.core.util.MapUtil; 033import com.mybatisflex.core.util.StringUtil; 034 035import java.lang.reflect.Field; 036import java.util.ArrayList; 037import java.util.Arrays; 038import java.util.HashMap; 039import java.util.HashSet; 040import java.util.List; 041import java.util.Map; 042import java.util.Set; 043import java.util.concurrent.ConcurrentHashMap; 044 045import static com.mybatisflex.core.query.QueryMethods.column; 046 047/** 048 * @author michael 049 */ 050@SuppressWarnings("rawtypes") 051public class RelationManager { 052 053 private RelationManager() { 054 } 055 056 private static Map<Class<?>, List<AbstractRelation>> classRelations = new ConcurrentHashMap<>(); 057 058 /** 059 * 默认查询深度 060 */ 061 private static int defaultQueryDepth = FlexGlobalConfig.getDefaultConfig().getDefaultRelationQueryDepth(); 062 063 /** 064 * 递归查询深度,默认为 2,在一些特殊场景下可以修改这个值 065 */ 066 private static ThreadLocal<Integer> depthThreadLocal = ThreadLocal.withInitial(() -> defaultQueryDepth); 067 068 /** 069 * 附加条件的查询参数 070 */ 071 private static ThreadLocal<Map<String, Object>> extraConditionParams = new ThreadLocal<>(); 072 073 074 /** 075 * 查询时,可忽略某些已经添加 Relation 注解的属性 076 */ 077 private static ThreadLocal<Set<String>> ignoreRelations = new ThreadLocal<>(); 078 079 080 /** 081 * 查询时,仅查询这个配置的 Relations 082 */ 083 private static ThreadLocal<Set<String>> onlyQueryRelations = new ThreadLocal<>(); 084 085 086 /** 087 * 每次查询是否自动清除 depth extraConditionParams ignoreRelations 的配置 088 */ 089 private static ThreadLocal<Boolean> autoClearConfig = ThreadLocal.withInitial(() -> true); 090 091 092 public static int getDefaultQueryDepth() { 093 return defaultQueryDepth; 094 } 095 096 public static void setDefaultQueryDepth(int defaultQueryDepth) { 097 RelationManager.defaultQueryDepth = defaultQueryDepth; 098 } 099 100 public static void setMaxDepth(int maxDepth) { 101 depthThreadLocal.set(maxDepth); 102 } 103 104 public static int getMaxDepth() { 105 return depthThreadLocal.get(); 106 } 107 108 public static void clearMaxDepth() { 109 depthThreadLocal.remove(); 110 } 111 112 113 public static void setExtraConditionParams(Map<String, Object> params) { 114 extraConditionParams.set(params); 115 } 116 117 public static void addExtraConditionParam(String key, Object value) { 118 Map<String, Object> params = extraConditionParams.get(); 119 if (params == null) { 120 params = new HashMap<>(); 121 extraConditionParams.set(params); 122 } 123 params.put(key, value); 124 } 125 126 public static Map<String, Object> getExtraConditionParams() { 127 return extraConditionParams.get(); 128 } 129 130 public static void clearExtraConditionParams() { 131 extraConditionParams.remove(); 132 } 133 134 135 //////ignore relations ////// 136 public static Set<String> getIgnoreRelations() { 137 return ignoreRelations.get(); 138 } 139 140 public static void setIgnoreRelations(Set<String> ignoreRelations) { 141 RelationManager.ignoreRelations.set(ignoreRelations); 142 } 143 144 145 public static <T> void addIgnoreRelations(LambdaGetter<T>... ignoreRelations) { 146 Set<String> relations = RelationManager.ignoreRelations.get(); 147 if (relations == null) { 148 relations = new HashSet<>(); 149 setIgnoreRelations(relations); 150 } 151 for (LambdaGetter<T> lambdaGetter : ignoreRelations) { 152 Class<?> implClass = LambdaUtil.getImplClass(lambdaGetter); 153 String fieldName = LambdaUtil.getFieldName(lambdaGetter); 154 relations.add(implClass.getSimpleName() + "." + fieldName); 155 } 156 } 157 158 public static void addIgnoreRelations(String... ignoreRelations) { 159 Set<String> relations = RelationManager.ignoreRelations.get(); 160 if (relations == null) { 161 relations = new HashSet<>(); 162 setIgnoreRelations(relations); 163 } 164 relations.addAll(Arrays.asList(ignoreRelations)); 165 } 166 167 168 public static void clearIgnoreRelations() { 169 ignoreRelations.remove(); 170 } 171 172 173 //////query relations ////// 174 public static Set<String> getQueryRelations() { 175 return onlyQueryRelations.get(); 176 } 177 178 public static void setQueryRelations(Set<String> queryRelations) { 179 RelationManager.onlyQueryRelations.set(queryRelations); 180 } 181 182 183 public static <T> void addQueryRelations(LambdaGetter<T>... queryRelations) { 184 Set<String> relations = RelationManager.onlyQueryRelations.get(); 185 if (relations == null) { 186 relations = new HashSet<>(); 187 setQueryRelations(relations); 188 } 189 for (LambdaGetter<T> lambdaGetter : queryRelations) { 190 Class<?> implClass = LambdaUtil.getImplClass(lambdaGetter); 191 String fieldName = LambdaUtil.getFieldName(lambdaGetter); 192 relations.add(implClass.getSimpleName() + "." + fieldName); 193 } 194 } 195 196 public static void addQueryRelations(String... queryRelations) { 197 Set<String> relations = RelationManager.onlyQueryRelations.get(); 198 if (relations == null) { 199 relations = new HashSet<>(); 200 setQueryRelations(relations); 201 } 202 relations.addAll(Arrays.asList(queryRelations)); 203 } 204 205 206 public static void clearQueryRelations() { 207 onlyQueryRelations.remove(); 208 } 209 210 211 public static void setAutoClearConfig(boolean enable) { 212 autoClearConfig.set(enable); 213 } 214 215 public static boolean getAutoClearConfig() { 216 return autoClearConfig.get(); 217 } 218 219 public static void clearAutoClearConfig() { 220 autoClearConfig.remove(); 221 } 222 223 224 static Object[] getExtraConditionParams(List<String> keys) { 225 if (keys == null || keys.isEmpty()) { 226 return FlexConsts.EMPTY_ARRAY; 227 } 228 Map<String, Object> paramMap = extraConditionParams.get(); 229 if (paramMap == null || paramMap.isEmpty()) { 230 return new Object[keys.size()]; 231 } 232 233 Object[] params = new Object[keys.size()]; 234 for (int i = 0; i < keys.size(); i++) { 235 params[i] = paramMap.get(keys.get(i)); 236 } 237 238 return params; 239 } 240 241 242 public static List<AbstractRelation> getRelations(Class<?> clazz) { 243 return MapUtil.computeIfAbsent(classRelations, clazz, RelationManager::doGetRelations); 244 } 245 246 private static List<AbstractRelation> doGetRelations(Class<?> entityClass) { 247 List<Field> allFields = ClassUtil.getAllFields(entityClass); 248 List<AbstractRelation> relations = new ArrayList<>(); 249 for (Field field : allFields) { 250 RelationManyToMany manyToManyAnnotation = field.getAnnotation(RelationManyToMany.class); 251 if (manyToManyAnnotation != null) { 252 relations.add(new ManyToMany<>(manyToManyAnnotation, entityClass, field)); 253 } 254 255 RelationManyToOne manyToOneAnnotation = field.getAnnotation(RelationManyToOne.class); 256 if (manyToOneAnnotation != null) { 257 relations.add(new ManyToOne<>(manyToOneAnnotation, entityClass, field)); 258 } 259 260 RelationOneToMany oneToManyAnnotation = field.getAnnotation(RelationOneToMany.class); 261 if (oneToManyAnnotation != null) { 262 relations.add(new OneToMany<>(oneToManyAnnotation, entityClass, field)); 263 } 264 265 RelationOneToOne oneToOneAnnotation = field.getAnnotation(RelationOneToOne.class); 266 if (oneToOneAnnotation != null) { 267 relations.add(new OneToOne<>(oneToOneAnnotation, entityClass, field)); 268 } 269 } 270 return relations; 271 } 272 273 274 public static <Entity> void queryRelations(BaseMapper<?> mapper, List<Entity> entities) { 275 try { 276 doQueryRelations(mapper, entities, 0, depthThreadLocal.get(), ignoreRelations.get(), onlyQueryRelations.get()); 277 } finally { 278 clearConfigIfNecessary(); 279 } 280 } 281 282 /** 283 * 清除查询配置 284 */ 285 public static void clearConfigIfNecessary() { 286 Boolean autoClearEnable = autoClearConfig.get(); 287 if (autoClearEnable != null && autoClearEnable) { 288 depthThreadLocal.remove(); 289 extraConditionParams.remove(); 290 onlyQueryRelations.remove(); 291 ignoreRelations.remove(); 292 } 293 } 294 295 296 @SuppressWarnings({"rawtypes", "unchecked"}) 297 static <Entity> void doQueryRelations(BaseMapper<?> mapper, List<Entity> entities, int currentDepth, int maxDepth, Set<String> ignoreRelations, Set<String> queryRelations) { 298 if (CollectionUtil.isEmpty(entities)) { 299 return; 300 } 301 302 if (currentDepth >= maxDepth) { 303 return; 304 } 305 306 Class<Entity> entityClass = (Class<Entity>) ClassUtil.getUsefulClass(entities.get(0).getClass()); 307 List<AbstractRelation> relations = getRelations(entityClass); 308 if (relations.isEmpty()) { 309 return; 310 } 311 312 relations.forEach(relation -> { 313 314 //ignore 315 if (ignoreRelations != null && (ignoreRelations.contains(relation.getSimpleName()) 316 || ignoreRelations.contains(relation.getName()))) { 317 return; 318 } 319 320 //only query 321 if (queryRelations != null && !queryRelations.isEmpty() 322 && !queryRelations.contains(relation.getSimpleName()) 323 && !queryRelations.contains(relation.getName())) { 324 return; 325 } 326 327 //注解配置的数据源 328 String relationDsKey = relation.getDataSource(); 329 if (StringUtil.hasText(relationDsKey)) { 330 DataSourceKey.use(relationDsKey); 331 } 332 333 try { 334 Set<Object> targetValues; 335 List<Row> mappingRows = null; 336 337 //通过中间表关联查询 338 if (relation.isRelationByMiddleTable()) { 339 340 Set selfFieldValues = relation.getSelfFieldValues(entities); 341 // 当数据对应的字段没有值的情况下,直接返回 342 if (selfFieldValues.isEmpty()) { 343 return; 344 } 345 QueryWrapper queryWrapper = QueryWrapper.create().select() 346 .from(relation.getJoinTable()); 347 if (selfFieldValues.size() > 1) { 348 queryWrapper.where(column(relation.getJoinSelfColumn()).in(selfFieldValues)); 349 } else { 350 queryWrapper.where(column(relation.getJoinSelfColumn()).eq(selfFieldValues.iterator().next())); 351 } 352 353 mappingRows = mapper.selectRowsByQuery(queryWrapper); 354 if (CollectionUtil.isEmpty(mappingRows)) { 355 return; 356 } 357 358 targetValues = new HashSet<>(); 359 360 for (Row mappingData : mappingRows) { 361 Object targetValue = mappingData.getIgnoreCase(relation.getJoinTargetColumn()); 362 if (targetValue != null) { 363 targetValues.add(targetValue); 364 } 365 } 366 } 367 //通过外键字段关联查询 368 else { 369 targetValues = relation.getSelfFieldValues(entities); 370 } 371 372 if (CollectionUtil.isEmpty(targetValues)) { 373 return; 374 } 375 376 //仅绑定字段:As目标实体类 不进行字段绑定:As映射类型 377 QueryWrapper queryWrapper = relation.buildQueryWrapper(targetValues); 378 List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relation.isOnlyQueryValueField() ? relation.getTargetEntityClass() : relation.getMappingType()); 379 if (CollectionUtil.isNotEmpty(targetObjectList)) { 380 381 //递归查询 382 doQueryRelations(mapper, targetObjectList, currentDepth + 1, maxDepth, ignoreRelations, queryRelations); 383 384 //进行内存 join 385 relation.join(entities, targetObjectList, mappingRows); 386 } 387 } finally { 388 if (StringUtil.hasText(relationDsKey)) { 389 DataSourceKey.clear(); 390 } 391 } 392 }); 393 } 394 395}