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