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