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