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.table.IdInfo;
022import com.mybatisflex.core.table.TableInfo;
023import com.mybatisflex.core.table.TableInfoFactory;
024import com.mybatisflex.core.util.ClassUtil;
025import com.mybatisflex.core.util.FieldWrapper;
026import com.mybatisflex.core.util.StringUtil;
027
028import java.lang.reflect.Field;
029import java.util.*;
030
031import static com.mybatisflex.core.query.QueryMethods.column;
032
033abstract class AbstractRelation<SelfEntity> {
034
035    protected String name;
036    protected String simpleName;
037    protected Class<SelfEntity> selfEntityClass;
038    protected Field relationField;
039    protected FieldWrapper relationFieldWrapper;
040
041    protected Field selfField;
042    protected FieldWrapper selfFieldWrapper;
043
044    protected String targetSchema;
045    protected String targetTable;
046    protected Field targetField;
047    protected Class<?> targetEntityClass;
048    protected TableInfo targetTableInfo;
049    protected FieldWrapper targetFieldWrapper;
050
051    protected String joinTable;
052    protected String joinSelfColumn;
053    protected String joinTargetColumn;
054
055    protected String dataSource;
056
057    protected String extraConditionSql;
058    protected List<String> extraConditionParamKeys;
059
060    public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField,
061                            String joinTable, String joinSelfColumn, String joinTargetColumn,
062                            String dataSource, Class<SelfEntity> entityClass, Field relationField,
063                            String extraCondition
064    ) {
065        this.name = entityClass.getSimpleName() + "." + relationField.getName();
066        this.simpleName = relationField.getName();
067        this.selfEntityClass = entityClass;
068        this.relationField = relationField;
069        this.relationFieldWrapper = FieldWrapper.of(entityClass, relationField.getName());
070
071        this.joinTable = joinTable;
072        this.joinSelfColumn = joinSelfColumn;
073        this.joinTargetColumn = joinTargetColumn;
074
075        this.dataSource = dataSource;
076
077        this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equals(selfField));
078        this.selfFieldWrapper = FieldWrapper.of(entityClass, selfField);
079
080
081        this.targetEntityClass = relationFieldWrapper.getMappingType();
082        this.targetSchema = targetSchema;
083        this.targetTable = targetTable;
084
085        this.targetField = ClassUtil.getFirstField(targetEntityClass, field -> field.getName().equals(targetField));
086        this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField);
087
088        this.targetTableInfo = TableInfoFactory.ofEntityClass(targetEntityClass);
089
090        initExtraCondition(extraCondition);
091    }
092
093    protected void initExtraCondition(String extraCondition) {
094        if (StringUtil.isBlank(extraCondition)) {
095            return;
096        }
097
098
099        List<String> sqlParamKeys = null;
100        char[] chars = extraCondition.toCharArray();
101        StringBuilder sqlBuilder = new StringBuilder();
102        sqlBuilder.append(chars[0]);
103        char prev, current;
104        boolean keyStart = false;
105        StringBuilder currentKey = null;
106        for (int i = 1; i < chars.length; i++) {
107            prev = chars[i - 1];
108            current = chars[i];
109            if (prev == ' ' && current == ':') {
110                keyStart = true;
111                currentKey = new StringBuilder();
112            } else if (keyStart) {
113                if (current != ' ' && current != ')') {
114                    currentKey.append(current);
115                } else {
116                    if (sqlParamKeys == null) {
117                        sqlParamKeys = new ArrayList<>();
118                    }
119                    sqlParamKeys.add(currentKey.toString());
120                    sqlBuilder.append("?").append(current);
121                    keyStart = false;
122                    currentKey = null;
123                }
124            } else {
125                sqlBuilder.append(current);
126            }
127        }
128        if (keyStart && currentKey != null && currentKey.length() > 0) {
129            if (sqlParamKeys == null) {
130                sqlParamKeys = new ArrayList<>();
131            }
132            sqlParamKeys.add(currentKey.toString());
133            sqlBuilder.append(" ?");
134        }
135
136        this.extraConditionSql = sqlBuilder.toString();
137        this.extraConditionParamKeys = sqlParamKeys != null ? sqlParamKeys : Collections.emptyList();
138    }
139
140    public String getName() {
141        return name;
142    }
143
144    public String getSimpleName() {
145        return simpleName;
146    }
147
148    public Class<SelfEntity> getSelfEntityClass() {
149        return selfEntityClass;
150    }
151
152    public void setSelfEntityClass(Class<SelfEntity> selfEntityClass) {
153        this.selfEntityClass = selfEntityClass;
154    }
155
156    public Field getRelationField() {
157        return relationField;
158    }
159
160    public void setRelationField(Field relationField) {
161        this.relationField = relationField;
162    }
163
164    public FieldWrapper getRelationFieldWrapper() {
165        return relationFieldWrapper;
166    }
167
168    public void setRelationFieldWrapper(FieldWrapper relationFieldWrapper) {
169        this.relationFieldWrapper = relationFieldWrapper;
170    }
171
172    public Field getSelfField() {
173        return selfField;
174    }
175
176    public void setSelfField(Field selfField) {
177        this.selfField = selfField;
178    }
179
180    public FieldWrapper getSelfFieldWrapper() {
181        return selfFieldWrapper;
182    }
183
184    public void setSelfFieldWrapper(FieldWrapper selfFieldWrapper) {
185        this.selfFieldWrapper = selfFieldWrapper;
186    }
187
188    public Field getTargetField() {
189        return targetField;
190    }
191
192    public void setTargetField(Field targetField) {
193        this.targetField = targetField;
194    }
195
196    public Class<?> getTargetEntityClass() {
197        return targetEntityClass;
198    }
199
200    public void setTargetEntityClass(Class<?> targetEntityClass) {
201        this.targetEntityClass = targetEntityClass;
202    }
203
204    public TableInfo getTargetTableInfo() {
205        return targetTableInfo;
206    }
207
208    public void setTargetTableInfo(TableInfo targetTableInfo) {
209        this.targetTableInfo = targetTableInfo;
210    }
211
212    public FieldWrapper getTargetFieldWrapper() {
213        return targetFieldWrapper;
214    }
215
216    public void setTargetFieldWrapper(FieldWrapper targetFieldWrapper) {
217        this.targetFieldWrapper = targetFieldWrapper;
218    }
219
220    public String getTargetSchema() {
221        return targetSchema;
222    }
223
224    public void setTargetSchema(String targetSchema) {
225        this.targetSchema = targetSchema;
226    }
227
228    public String getTargetTable() {
229        return targetTable;
230    }
231
232    public void setTargetTable(String targetTable) {
233        this.targetTable = targetTable;
234    }
235
236    public String getJoinTable() {
237        return joinTable;
238    }
239
240    public void setJoinTable(String joinTable) {
241        this.joinTable = joinTable;
242    }
243
244    public String getJoinSelfColumn() {
245        return joinSelfColumn;
246    }
247
248    public void setJoinSelfColumn(String joinSelfColumn) {
249        this.joinSelfColumn = joinSelfColumn;
250    }
251
252    public String getJoinTargetColumn() {
253        return joinTargetColumn;
254    }
255
256    public void setJoinTargetColumn(String joinTargetColumn) {
257        this.joinTargetColumn = joinTargetColumn;
258    }
259
260    public Set<Object> getSelfFieldValues(List<SelfEntity> selfEntities) {
261        if (selfEntities == null || selfEntities.isEmpty()) {
262            return Collections.emptySet();
263        }
264        Set<Object> values = new LinkedHashSet<>();
265        selfEntities.forEach(self -> {
266            Object value = selfFieldWrapper.get(self);
267            if (value != null && !"".equals(value)) {
268                values.add(value);
269            }
270        });
271        return values;
272    }
273
274
275    public Class<?> getMappingType() {
276        return relationFieldWrapper.getMappingType();
277    }
278
279    public String getDataSource() {
280        return dataSource;
281    }
282
283    public void setDataSource(String dataSource) {
284        this.dataSource = dataSource;
285    }
286
287    public String getTargetTableWithSchema() {
288        if (StringUtil.isNotBlank(targetTable)) {
289            return StringUtil.isNotBlank(targetSchema) ? targetSchema + "." + targetTable : targetTable;
290        } else {
291            return targetTableInfo.getTableNameWithSchema();
292        }
293    }
294
295    protected boolean isRelationByMiddleTable() {
296        return StringUtil.isNotBlank(joinTable);
297    }
298
299
300    protected static Class<?> getTargetEntityClass(Class<?> entityClass, Field relationField) {
301        return FieldWrapper.of(entityClass, relationField.getName()).getMappingType();
302    }
303
304    protected static String getDefaultPrimaryProperty(String key, Class<?> entityClass, String message) {
305        if (StringUtil.isNotBlank(key)) {
306            return key;
307        }
308
309        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass);
310        List<IdInfo> primaryKeyList = tableInfo.getPrimaryKeyList();
311        if (primaryKeyList == null || primaryKeyList.size() != 1) {
312            throw FlexExceptions.wrap(message);
313        }
314
315        return primaryKeyList.get(0).getProperty();
316    }
317
318
319    /**
320     * 构建查询目标对象的 QueryWrapper
321     *
322     * @param targetValues 条件的值
323     * @return QueryWrapper
324     */
325    public QueryWrapper buildQueryWrapper(Set<Object> targetValues) {
326        QueryWrapper queryWrapper = QueryWrapper.create()
327            .select()
328            .from(getTargetTableWithSchema());
329
330        if (targetValues.size() > 1) {
331            queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).in(targetValues));
332        } else {
333            queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).eq(targetValues.iterator().next()));
334        }
335
336        if (StringUtil.isNotBlank(extraConditionSql)) {
337            queryWrapper.and(extraConditionSql, RelationManager.getExtraConditionParams(extraConditionParamKeys));
338        }
339
340        customizeQueryWrapper(queryWrapper);
341
342        return queryWrapper;
343    }
344
345
346    /**
347     * 方便子类最近自定义的条件
348     *
349     * @param queryWrapper 查询条件
350     */
351    public void customizeQueryWrapper(QueryWrapper queryWrapper) {
352        //do thing
353    }
354
355
356    /**
357     * @param selfEntities     当前的实体类列表
358     * @param targetObjectList 查询到的结果
359     * @param mappingRows      中间表的映射数据,非中间表查询的场景下,mappingRows 永远为 null
360     */
361    public abstract void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows);
362
363}