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.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        String currentDsKey = DataSourceKey.get();
313        try {
314            relations.forEach(relation -> {
315
316                //ignore
317                if (ignoreRelations != null && (ignoreRelations.contains(relation.getSimpleName())
318                    || ignoreRelations.contains(relation.getName()))) {
319                    return;
320                }
321
322                //only query
323                if (queryRelations != null && !queryRelations.isEmpty()
324                    && !queryRelations.contains(relation.getSimpleName())
325                    && !queryRelations.contains(relation.getName())) {
326                    return;
327                }
328
329                //注解配置的数据源
330                String configDsKey = relation.getDataSource();
331                if (StringUtil.isBlank(configDsKey) && currentDsKey != null) {
332                    configDsKey = currentDsKey;
333                }
334
335                try {
336                    if (StringUtil.isNotBlank(configDsKey)) {
337                        DataSourceKey.use(configDsKey);
338                    }
339
340                    Set<Object> targetValues;
341                    List<Row> mappingRows = null;
342
343                    //通过中间表关联查询
344                    if (relation.isRelationByMiddleTable()) {
345
346                        Set selfFieldValues = relation.getSelfFieldValues(entities);
347                        // 当数据对应的字段没有值的情况下,直接返回
348                        if (selfFieldValues.isEmpty()) {
349                            return;
350                        }
351                        QueryWrapper queryWrapper = QueryWrapper.create().select()
352                            .from(relation.getJoinTable());
353                        if (selfFieldValues.size() > 1) {
354                            queryWrapper.where(column(relation.getJoinSelfColumn()).in(selfFieldValues));
355                        } else {
356                            queryWrapper.where(column(relation.getJoinSelfColumn()).eq(selfFieldValues.iterator().next()));
357                        }
358
359                        mappingRows = mapper.selectListByQueryAs(queryWrapper, Row.class);
360                        if (CollectionUtil.isEmpty(mappingRows)) {
361                            return;
362                        }
363
364                        targetValues = new HashSet<>();
365
366                        for (Row mappingData : mappingRows) {
367                            Object targetValue = mappingData.getIgnoreCase(relation.getJoinTargetColumn());
368                            if (targetValue != null) {
369                                targetValues.add(targetValue);
370                            }
371                        }
372                    }
373                    //通过外键字段关联查询
374                    else {
375                        targetValues = relation.getSelfFieldValues(entities);
376                    }
377
378                    if (CollectionUtil.isEmpty(targetValues)) {
379                        return;
380                    }
381
382                    //仅绑定字段:As目标实体类 不进行字段绑定:As映射类型
383                    QueryWrapper queryWrapper = relation.buildQueryWrapper(targetValues);
384                    List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relation.isOnlyQueryValueField() ? relation.getTargetEntityClass() : relation.getMappingType());
385                    if (CollectionUtil.isNotEmpty(targetObjectList)) {
386
387                        //递归查询
388                        doQueryRelations(mapper, targetObjectList, currentDepth + 1, maxDepth, ignoreRelations, queryRelations);
389
390                        //进行内存 join
391                        relation.join(entities, targetObjectList, mappingRows);
392                    }
393                } finally {
394                    if (StringUtil.isNotBlank(configDsKey)) {
395                        DataSourceKey.clear();
396                    }
397                }
398            });
399        } finally {
400            if (currentDsKey != null) {
401                DataSourceKey.use(currentDsKey);
402            }
403        }
404    }
405
406}