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.QueryWrapper;
020import com.mybatisflex.core.row.Row;
021import com.mybatisflex.core.util.*;
022
023import java.lang.reflect.Field;
024import java.util.*;
025import java.util.function.Function;
026
027public class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
028
029    protected String mapKeyField;
030
031    protected FieldWrapper mapKeyFieldWrapper;
032
033    protected String orderBy;
034
035    protected long limit = 0;
036
037    protected String selfValueSplitBy;
038
039
040    public ToManyRelation(String selfField, String targetSchema, String targetTable, String targetField, String valueField,
041                          String joinTable, String joinSelfColumn, String joinTargetColumn,
042                          String dataSource, Class<SelfEntity> selfEntityClass, Field relationField,
043                          String extraCondition, String[] selectColumns) {
044        super(selfField, targetSchema, targetTable, targetField, valueField,
045            joinTable, joinSelfColumn, joinTargetColumn,
046            dataSource, selfEntityClass, relationField,
047            extraCondition, selectColumns
048        );
049    }
050
051    public static Class<? extends Map> getMapWrapType(Class<?> type) {
052        if (ClassUtil.canInstance(type.getModifiers())) {
053            return (Class<? extends Map>) type;
054        }
055
056        return HashMap.class;
057    }
058
059    /**
060     * 构建查询目标对象的 QueryWrapper
061     *
062     * @param targetValues 条件的值
063     * @return QueryWrapper
064     */
065    @Override
066    public QueryWrapper buildQueryWrapper(Set<Object> targetValues) {
067        if (StringUtil.isNotBlank(selfValueSplitBy) && CollectionUtil.isNotEmpty(targetValues)) {
068            Set<Object> newTargetValues = new HashSet<>();
069            for (Object targetValue : targetValues) {
070                if (targetValue == null) {
071                    continue;
072                }
073                if (!(targetValue instanceof String)) {
074                    throw FlexExceptions.wrap("split field only support String type, but current type is: \"" + targetValue.getClass().getName() + "\"");
075                }
076                String[] splitValues = ((String) targetValue).split(selfValueSplitBy);
077                for (String splitValue : splitValues) {
078                    //优化分割后的数据类型(防止在数据库查询时候出现隐式转换)
079                    newTargetValues.add(ConvertUtil.convert(splitValue, targetFieldWrapper.getFieldType()));
080                }
081            }
082            targetValues = newTargetValues;
083        }
084        return super.buildQueryWrapper(targetValues);
085    }
086
087    @Override
088    public void customizeQueryWrapper(QueryWrapper queryWrapper) {
089        if (StringUtil.isNotBlank(orderBy)) {
090            queryWrapper.orderBy(orderBy);
091        }
092
093        if (limit > 0) {
094            queryWrapper.limit(limit);
095        }
096    }
097
098    @SuppressWarnings("rawtypes")
099    @Override
100    public void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows) {
101
102        //目标表关联字段->目标表对象
103        Map<String, List<Object>> leftFieldToRightTableMap = new HashMap<>(targetObjectList.size());
104        for (Object targetObject : targetObjectList) {
105            Object targetJoinFieldValue = targetFieldWrapper.get(targetObject);
106            if (targetJoinFieldValue != null) {
107                leftFieldToRightTableMap.computeIfAbsent(targetJoinFieldValue.toString(), k -> new ArrayList<>(1)).add(targetObject);
108            }
109        }
110
111        //通过中间表
112        if (mappingRows != null) {
113            //当使用中间表时,需要重新映射关联关系
114            Map<String, List<Object>> temp = new HashMap<>(selfEntities.size());
115            for (Row mappingRow : mappingRows) {
116                Object midTableJoinSelfValue = mappingRow.getIgnoreCase(joinSelfColumn);
117                if (midTableJoinSelfValue == null) {
118                    continue;
119                }
120                Object midTableJoinTargetValue = mappingRow.getIgnoreCase(joinTargetColumn);
121                if (midTableJoinTargetValue == null) {
122                    continue;
123                }
124                List<Object> targetObjects = leftFieldToRightTableMap.get(midTableJoinTargetValue.toString());
125                if (targetObjects == null) {
126                    continue;
127                }
128                temp.computeIfAbsent(midTableJoinSelfValue.toString(), k -> new ArrayList<>(targetObjects.size())).addAll(targetObjects);
129            }
130            leftFieldToRightTableMap = temp;
131        }
132
133
134        //关联集合的类型
135        Class<?> fieldType = relationFieldWrapper.getFieldType();
136        boolean isMapType = Map.class.isAssignableFrom(fieldType);
137        Class<?> wrapType = isMapType ? getMapWrapType(fieldType) : MapperUtil.getCollectionWrapType(fieldType);
138        boolean splitMode = StringUtil.isNotBlank(selfValueSplitBy);
139
140        for (SelfEntity selfEntity : selfEntities) {
141            if (selfEntity == null) {
142                continue;
143            }
144            Object selfValue = selfFieldWrapper.get(selfEntity);
145            if (selfValue == null) {
146                continue;
147            }
148            selfValue = selfValue.toString();
149
150            //只有当splitBy不为空时才会有多个值
151            Set<String> targetMappingValues;
152
153            if (splitMode) {
154                String[] splitValues = ((String) selfValue).split(selfValueSplitBy);
155                targetMappingValues = new LinkedHashSet<>(Arrays.asList(splitValues));
156            } else {
157                targetMappingValues = new HashSet<>(1);
158                targetMappingValues.add((String) selfValue);
159            }
160
161            if (targetMappingValues.isEmpty()) {
162                return;
163            }
164
165            // map
166            if (isMapType) {
167                Map map = (Map) ClassUtil.newInstance(wrapType);
168                Set<Object> validateCountSet = new HashSet<>(targetMappingValues.size());
169                for (String targetMappingValue : targetMappingValues) {
170                    List<Object> targetObjects = leftFieldToRightTableMap.get(targetMappingValue);
171                    //如果非真实外键约束 可能没有对应的对象
172                    if (targetObjects == null) {
173                        continue;
174                    }
175                    for (Object targetObject : targetObjects) {
176                        Object keyValue = mapKeyFieldWrapper.get(targetObject);
177                        Object needKeyValue = ConvertUtil.convert(keyValue, relationFieldWrapper.getKeyType());
178                        if (validateCountSet.contains(needKeyValue)) {
179                            //当字段类型为Map时,一个key对应的value只能有一个
180                            throw FlexExceptions.wrap("When fieldType is Map, the target entity can only be one,\n" +
181                                " current entity type is : " + selfEntity + "\n" +
182                                " relation field name is : " + relationField.getName() + "\n" +
183                                " target entity is : " + targetObjects);
184                        }
185                        validateCountSet.add(needKeyValue);
186
187                        //noinspection unchecked
188                        map.put(needKeyValue, targetObject);
189                    }
190                }
191                if (!map.isEmpty()) {
192                    relationFieldWrapper.set(map, selfEntity);
193                }
194
195            }
196            //集合
197            else {
198                Collection collection = (Collection) ClassUtil.newInstance(wrapType);
199                if (onlyQueryValueField) {
200                    Object first = targetObjectList.iterator().next();
201                    //将getter方法用单独的变量存储 FieldWrapper.of虽然有缓存 但每次调用至少有一个HashMap的get开销
202                    FieldWrapper fieldValueFieldWrapper = FieldWrapper.of(first.getClass(), valueField);
203                    for (String targetMappingValue : targetMappingValues) {
204                        List<Object> targetObjects = leftFieldToRightTableMap.get(targetMappingValue);
205                        if (targetObjects == null) {
206                            continue;
207                        }
208                        for (Object targetObject : targetObjects) {
209                            //仅绑定某个字段
210                            //noinspection unchecked
211                            collection.add(fieldValueFieldWrapper.get(targetObject));
212                        }
213                    }
214                } else {
215                    for (String targetMappingValue : targetMappingValues) {
216                        List<Object> targetObjects = leftFieldToRightTableMap.get(targetMappingValue);
217                        if (targetObjects == null) {
218                            continue;
219                        }
220                        //noinspection unchecked
221                        collection.addAll(targetObjects);
222                    }
223                }
224
225                relationFieldWrapper.set(collection, selfEntity);
226            }
227        }
228
229    }
230
231    public void setMapKeyField(String mapKeyField) {
232        this.mapKeyField = mapKeyField;
233        if (StringUtil.isNotBlank(mapKeyField)) {
234            this.mapKeyFieldWrapper = FieldWrapper.of(targetEntityClass, mapKeyField);
235        } else {
236            if (Map.class.isAssignableFrom(relationFieldWrapper.getFieldType())) {
237                throw FlexExceptions.wrap("Please config mapKeyField for map field: " + relationFieldWrapper.getField());
238            }
239        }
240    }
241
242}