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.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}