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}