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.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.StringUtil; 031import org.apache.ibatis.util.MapUtil; 032 033import java.lang.reflect.Field; 034import java.util.*; 035import java.util.concurrent.ConcurrentHashMap; 036 037import static com.mybatisflex.core.query.QueryMethods.column; 038 039/** 040 * @author michael 041 */ 042@SuppressWarnings("rawtypes") 043public class RelationManager { 044 045 private RelationManager() { 046 } 047 048 private static Map<Class<?>, List<AbstractRelation>> classRelations = new ConcurrentHashMap<>(); 049 050 /** 051 * 默认查询深度 052 */ 053 private static int defaultQueryDepth = FlexGlobalConfig.getDefaultConfig().getDefaultRelationQueryDepth(); 054 055 /** 056 * 递归查询深度,默认为 2,在一些特殊场景下可以修改这个值 057 */ 058 private static ThreadLocal<Integer> depthThreadLocal = ThreadLocal.withInitial(() -> defaultQueryDepth); 059 060 /** 061 * 附加条件的查询参数 062 */ 063 private static ThreadLocal<Map<String, Object>> extraConditionParams = new ThreadLocal<>(); 064 065 066 /** 067 * 查询时,可忽略某些已经添加 Relation 注解的属性 068 */ 069 private static ThreadLocal<Set<String>> ignoreRelations = new ThreadLocal<>(); 070 071 072 /** 073 * 每次查询是否自动清除 depth extraConditionParams ignoreRelations 的配置 074 */ 075 private static ThreadLocal<Boolean> autoClearConfig = ThreadLocal.withInitial(() -> true); 076 077 public static int getDefaultQueryDepth() { 078 return defaultQueryDepth; 079 } 080 081 public static void setDefaultQueryDepth(int defaultQueryDepth) { 082 RelationManager.defaultQueryDepth = defaultQueryDepth; 083 } 084 085 public static void setMaxDepth(int maxDepth) { 086 depthThreadLocal.set(maxDepth); 087 } 088 089 public static int getMaxDepth() { 090 return depthThreadLocal.get(); 091 } 092 093 public static void clearMaxDepth() { 094 extraConditionParams.remove(); 095 } 096 097 098 public static void setExtraConditionParams(Map<String, Object> params) { 099 extraConditionParams.set(params); 100 } 101 102 public static void addExtraConditionParam(String key, String value) { 103 Map<String, Object> params = extraConditionParams.get(); 104 if (params == null) { 105 params = new HashMap<>(); 106 extraConditionParams.set(params); 107 } 108 params.put(key, value); 109 } 110 111 public static Map<String, Object> getExtraConditionParams() { 112 return extraConditionParams.get(); 113 } 114 115 public static void clearExtraConditionParams() { 116 extraConditionParams.remove(); 117 } 118 119 120 public static void setIgnoreRelations(Set<String> ignoreRelations) { 121 RelationManager.ignoreRelations.set(ignoreRelations); 122 } 123 124 public static void addIgnoreRelations(String... ignoreRelations) { 125 Set<String> relations = RelationManager.ignoreRelations.get(); 126 if (relations == null) { 127 relations = new HashSet<>(); 128 setIgnoreRelations(relations); 129 } 130 relations.addAll(Arrays.asList(ignoreRelations)); 131 } 132 133 public static Set<String> getIgnoreRelations() { 134 return ignoreRelations.get(); 135 } 136 137 public static void clearIgnoreRelations() { 138 ignoreRelations.remove(); 139 } 140 141 142 public static void setAutoClearConfig(boolean enable) { 143 autoClearConfig.set(enable); 144 } 145 146 public static boolean getAutoClearConfig() { 147 return autoClearConfig.get(); 148 } 149 150 public static void clearAutoClearConfig() { 151 autoClearConfig.remove(); 152 } 153 154 155 static Object[] getExtraConditionParams(List<String> keys) { 156 if (keys == null || keys.isEmpty()) { 157 return FlexConsts.EMPTY_ARRAY; 158 } 159 Map<String, Object> paramMap = extraConditionParams.get(); 160 if (paramMap == null || paramMap.isEmpty()) { 161 return new Object[keys.size()]; 162 } 163 164 Object[] params = new Object[keys.size()]; 165 for (int i = 0; i < keys.size(); i++) { 166 params[i] = paramMap.get(keys.get(i)); 167 } 168 169 return params; 170 } 171 172 173 private static List<AbstractRelation> getRelations(Class<?> clazz) { 174 return MapUtil.computeIfAbsent(classRelations, clazz, RelationManager::doGetRelations); 175 } 176 177 private static List<AbstractRelation> doGetRelations(Class<?> entityClass) { 178 List<Field> allFields = ClassUtil.getAllFields(entityClass); 179 List<AbstractRelation> relations = new ArrayList<>(); 180 for (Field field : allFields) { 181 RelationManyToMany manyToManyAnnotation = field.getAnnotation(RelationManyToMany.class); 182 if (manyToManyAnnotation != null) { 183 relations.add(new ManyToMany<>(manyToManyAnnotation, entityClass, field)); 184 } 185 186 RelationManyToOne manyToOneAnnotation = field.getAnnotation(RelationManyToOne.class); 187 if (manyToOneAnnotation != null) { 188 relations.add(new ManyToOne<>(manyToOneAnnotation, entityClass, field)); 189 } 190 191 RelationOneToMany oneToManyAnnotation = field.getAnnotation(RelationOneToMany.class); 192 if (oneToManyAnnotation != null) { 193 relations.add(new OneToMany<>(oneToManyAnnotation, entityClass, field)); 194 } 195 196 RelationOneToOne oneToOneAnnotation = field.getAnnotation(RelationOneToOne.class); 197 if (oneToOneAnnotation != null) { 198 relations.add(new OneToOne<>(oneToOneAnnotation, entityClass, field)); 199 } 200 } 201 return relations; 202 } 203 204 205 public static <Entity> void queryRelations(BaseMapper<?> mapper, List<Entity> entities) { 206 doQueryRelations(mapper, entities, 0, depthThreadLocal.get(), ignoreRelations.get()); 207 clearConfigIfNecessary(); 208 } 209 210 /** 211 * 清除查询配置 212 */ 213 private static void clearConfigIfNecessary() { 214 Boolean autoClearEnable = autoClearConfig.get(); 215 if (autoClearEnable != null && autoClearEnable) { 216 depthThreadLocal.remove(); 217 extraConditionParams.remove(); 218 ignoreRelations.remove(); 219 } 220 } 221 222 223 @SuppressWarnings({"rawtypes", "unchecked"}) 224 static <Entity> void doQueryRelations(BaseMapper<?> mapper, List<Entity> entities, int currentDepth, int maxDepth, Set<String> ignoreRelations) { 225 if (CollectionUtil.isEmpty(entities)) { 226 return; 227 } 228 229 if (currentDepth >= maxDepth) { 230 return; 231 } 232 233 Class<Entity> entityClass = (Class<Entity>) ClassUtil.getUsefulClass(entities.get(0).getClass()); 234 List<AbstractRelation> relations = getRelations(entityClass); 235 if (relations.isEmpty()) { 236 return; 237 } 238 239 String currentDsKey = DataSourceKey.get(); 240 try { 241 relations.forEach(relation -> { 242 243 //ignore 244 if (ignoreRelations != null && (ignoreRelations.contains(relation.getSimpleName()) 245 || ignoreRelations.contains(relation.getName()))) { 246 return; 247 } 248 249 Set<Object> targetValues; 250 List<Row> mappingRows = null; 251 252 //通过中间表关联查询 253 if (relation.isRelationByMiddleTable()) { 254 targetValues = new HashSet<>(); 255 Set selfFieldValues = relation.getSelfFieldValues(entities); 256 QueryWrapper queryWrapper = QueryWrapper.create().select() 257 .from(relation.getJoinTable()); 258 if (selfFieldValues.size() > 1) { 259 queryWrapper.where(column(relation.getJoinSelfColumn()).in(selfFieldValues)); 260 } else { 261 queryWrapper.where(column(relation.getJoinSelfColumn()).eq(selfFieldValues.iterator().next())); 262 } 263 264 mappingRows = mapper.selectListByQueryAs(queryWrapper, Row.class); 265 if (CollectionUtil.isEmpty(mappingRows)) { 266 return; 267 } 268 269 for (Row mappingData : mappingRows) { 270 Object targetValue = mappingData.getIgnoreCase(relation.getJoinTargetColumn()); 271 if (targetValue != null) { 272 targetValues.add(targetValue); 273 } 274 } 275 } 276 //通过外键字段关联查询 277 else { 278 targetValues = relation.getSelfFieldValues(entities); 279 } 280 281 if (CollectionUtil.isEmpty(targetValues)) { 282 return; 283 } 284 285 //注解配置的数据源 286 String configDsKey = relation.getDataSource(); 287 if (StringUtil.isBlank(configDsKey) && currentDsKey != null) { 288 configDsKey = currentDsKey; 289 } 290 291 try { 292 if (StringUtil.isNotBlank(configDsKey)) { 293 DataSourceKey.use(configDsKey); 294 } 295 296 QueryWrapper queryWrapper = relation.buildQueryWrapper(targetValues); 297 List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relation.getMappingType()); 298 if (CollectionUtil.isNotEmpty(targetObjectList)) { 299 300 //递归查询 301 doQueryRelations(mapper, targetObjectList, currentDepth + 1, maxDepth, ignoreRelations); 302 303 //进行内存 join 304 relation.join(entities, targetObjectList, mappingRows); 305 } 306 } finally { 307 if (StringUtil.isNotBlank(configDsKey)) { 308 DataSourceKey.clear(); 309 } 310 } 311 }); 312 } finally { 313 if (currentDsKey != null) { 314 DataSourceKey.use(currentDsKey); 315 } 316 } 317 } 318 319}