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