
com.mybatisflex.core.relation.AbstractRelation Maven / Gradle / Ivy
/*
* Copyright (c) 2022-2025, Mybatis-Flex ([email protected]).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.relation;
import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.table.IdInfo;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.util.ArrayUtil;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.FieldWrapper;
import com.mybatisflex.core.util.StringUtil;
import java.lang.reflect.Field;
import java.util.*;
import static com.mybatisflex.core.query.QueryMethods.column;
public abstract class AbstractRelation {
protected String name;
protected String simpleName;
protected Class selfEntityClass;
protected Field relationField;
protected FieldWrapper relationFieldWrapper;
protected Field selfField;
protected FieldWrapper selfFieldWrapper;
protected String targetSchema;
protected String targetTable;
protected Field targetField;
protected String valueField;
protected boolean onlyQueryValueField;
protected Class> targetEntityClass;
protected TableInfo targetTableInfo;
protected FieldWrapper targetFieldWrapper;
protected String joinTable;
protected String joinSelfColumn;
protected String joinTargetColumn;
protected String dataSource;
protected String extraConditionSql;
protected List extraConditionParamKeys;
protected QueryColumn conditionColumn;
protected String[] selectColumns;
public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField, String valueField,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class entityClass, Field relationField,
String extraCondition, String[] selectColumns
) {
this.name = entityClass.getSimpleName() + "." + relationField.getName();
this.simpleName = relationField.getName();
this.selfEntityClass = entityClass;
this.relationField = relationField;
this.relationFieldWrapper = FieldWrapper.of(entityClass, relationField.getName());
this.joinTable = joinTable;
this.joinSelfColumn = joinSelfColumn;
this.joinTargetColumn = joinTargetColumn;
this.dataSource = dataSource;
this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equalsIgnoreCase(selfField));
this.selfFieldWrapper = FieldWrapper.of(entityClass, selfField);
//以使用者注解配置为主
this.targetTableInfo = StringUtil.noText(targetTable) ? TableInfoFactory.ofEntityClass(relationFieldWrapper.getMappingType()) : TableInfoFactory.ofTableName(targetTable);
this.targetSchema = targetTableInfo != null ? targetTableInfo.getSchema() : targetSchema;
this.targetTable = targetTableInfo != null ? targetTableInfo.getTableName() : targetTable;
//当指定了 valueField 的时候,一般是 String Integer 等基本数据类型
this.targetEntityClass = (StringUtil.hasText(valueField) && targetTableInfo != null) ? targetTableInfo.getEntityClass() : relationFieldWrapper.getMappingType();
this.targetField = ClassUtil.getFirstField(targetEntityClass, field -> field.getName().equalsIgnoreCase(targetField));
if (this.targetField == null) {
throw new IllegalStateException("Can not find field by name \"" + targetField + "\" from class: " + targetEntityClass.getName());
}
this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField);
this.valueField = valueField;
this.onlyQueryValueField = StringUtil.hasText(valueField);
this.conditionColumn = targetTableInfo == null ? column(targetTable, StringUtil.camelToUnderline(this.targetField.getName()))
: column(targetTable, targetTableInfo.getColumnByProperty(this.targetField.getName()));
if (onlyQueryValueField) {
//仅绑定字段时只需要查询关联列和该字段列即可
this.selectColumns = new String[]{conditionColumn.getName(), targetTableInfo != null ? targetTableInfo.getColumnByProperty(this.valueField) : StringUtil.camelToUnderline(this.valueField)};
} else {
if (ArrayUtil.isNotEmpty(selectColumns)) {
if (ArrayUtil.contains(selectColumns, conditionColumn.getName())) {
this.selectColumns = selectColumns;
} else {
//需要追加 conditionColumn,因为进行内存 join 的时候,需要用到这个内容进行对比
this.selectColumns = ArrayUtil.concat(selectColumns, new String[]{conditionColumn.getName()});
}
}
}
initExtraCondition(extraCondition);
}
protected void initExtraCondition(String extraCondition) {
if (StringUtil.noText(extraCondition)) {
return;
}
List sqlParamKeys = null;
char[] chars = extraCondition.toCharArray();
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(chars[0]);
char prev, current;
boolean keyStart = false;
StringBuilder currentKey = null;
for (int i = 1; i < chars.length; i++) {
prev = chars[i - 1];
current = chars[i];
if (prev == ' ' && current == ':') {
keyStart = true;
currentKey = new StringBuilder();
} else if (keyStart) {
if (current != ' ' && current != ')') {
currentKey.append(current);
} else {
if (sqlParamKeys == null) {
sqlParamKeys = new ArrayList<>();
}
sqlParamKeys.add(currentKey.toString());
sqlBuilder.append("?").append(current);
keyStart = false;
currentKey = null;
}
} else {
sqlBuilder.append(current);
}
}
if (keyStart && currentKey != null && currentKey.length() > 0) {
if (sqlParamKeys == null) {
sqlParamKeys = new ArrayList<>();
}
sqlParamKeys.add(currentKey.toString());
sqlBuilder.append(" ?");
}
this.extraConditionSql = sqlBuilder.toString();
this.extraConditionParamKeys = sqlParamKeys != null ? sqlParamKeys : Collections.emptyList();
}
public String getName() {
return name;
}
public String getSimpleName() {
return simpleName;
}
public Class getSelfEntityClass() {
return selfEntityClass;
}
public void setSelfEntityClass(Class selfEntityClass) {
this.selfEntityClass = selfEntityClass;
}
public Field getRelationField() {
return relationField;
}
public void setRelationField(Field relationField) {
this.relationField = relationField;
}
public FieldWrapper getRelationFieldWrapper() {
return relationFieldWrapper;
}
public void setRelationFieldWrapper(FieldWrapper relationFieldWrapper) {
this.relationFieldWrapper = relationFieldWrapper;
}
public Field getSelfField() {
return selfField;
}
public void setSelfField(Field selfField) {
this.selfField = selfField;
}
public FieldWrapper getSelfFieldWrapper() {
return selfFieldWrapper;
}
public void setSelfFieldWrapper(FieldWrapper selfFieldWrapper) {
this.selfFieldWrapper = selfFieldWrapper;
}
public Field getTargetField() {
return targetField;
}
public void setTargetField(Field targetField) {
this.targetField = targetField;
}
public Class> getTargetEntityClass() {
return targetEntityClass;
}
public void setTargetEntityClass(Class> targetEntityClass) {
this.targetEntityClass = targetEntityClass;
}
public TableInfo getTargetTableInfo() {
return targetTableInfo;
}
public void setTargetTableInfo(TableInfo targetTableInfo) {
this.targetTableInfo = targetTableInfo;
}
public FieldWrapper getTargetFieldWrapper() {
return targetFieldWrapper;
}
public void setTargetFieldWrapper(FieldWrapper targetFieldWrapper) {
this.targetFieldWrapper = targetFieldWrapper;
}
public String getTargetSchema() {
return targetSchema;
}
public void setTargetSchema(String targetSchema) {
this.targetSchema = targetSchema;
}
public String getTargetTable() {
return targetTable;
}
public void setTargetTable(String targetTable) {
this.targetTable = targetTable;
}
public String getValueField() {
return valueField;
}
public void setValueField(String valueField) {
this.valueField = valueField;
}
public boolean isOnlyQueryValueField() {
return onlyQueryValueField;
}
public void setOnlyQueryValueField(boolean onlyQueryValueField) {
this.onlyQueryValueField = onlyQueryValueField;
}
public String getJoinTable() {
return joinTable;
}
public void setJoinTable(String joinTable) {
this.joinTable = joinTable;
}
public String getJoinSelfColumn() {
return joinSelfColumn;
}
public void setJoinSelfColumn(String joinSelfColumn) {
this.joinSelfColumn = joinSelfColumn;
}
public String getJoinTargetColumn() {
return joinTargetColumn;
}
public void setJoinTargetColumn(String joinTargetColumn) {
this.joinTargetColumn = joinTargetColumn;
}
public Set