Merge remote-tracking branch 'origin/main'

# Conflicts:
#	mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToMany.java
#	mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToOne.java
#	mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToMany.java
#	mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToOne.java
This commit is contained in:
Suomm 2023-07-13 08:09:11 +08:00
commit 7b302abc8a
18 changed files with 636 additions and 277 deletions

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.annotation;
import java.lang.annotation.*;
/**
* 附件条件
*
* @author michael
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Condition {
/**
* 列名
*/
String column();
/**
* 逻辑值> , >= , = , IS NULL, IS NOT NULL
*/
String logic() default " = ";
/**
*
*/
String[] value() default {};
}

View File

@ -35,26 +35,14 @@ public @interface RelationManyToMany {
String selfField() default ""; String selfField() default "";
/** /**
* <p> * 目标实体类对应的表的 schema一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表的 schema 模式
*
* <p>
* 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema
* 需要在这里指定对应表的 schema 一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return schema 名称 * @return schema 名称
*/ */
String targetSchema() default ""; String targetSchema() default "";
/** /**
* <p> * 目标实体类对应的表一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表名
*
* <p>
* 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时
* 需要在这里指定对应表的表名一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return 表名 * @return 表名
*/ */
@ -88,6 +76,11 @@ public @interface RelationManyToMany {
*/ */
String joinTargetColumn(); String joinTargetColumn();
/**
* 查询时追加的额外条件
*/
Condition[] extraConditions() default {};
/** /**
* 查询排序 * 查询排序
* *
@ -102,4 +95,5 @@ public @interface RelationManyToMany {
*/ */
String dataSource() default ""; String dataSource() default "";
} }

View File

@ -35,26 +35,14 @@ public @interface RelationManyToOne {
String selfField(); String selfField();
/** /**
* <p> * 目标实体类对应的表的 schema一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表的 schema 模式
*
* <p>
* 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema
* 需要在这里指定对应表的 schema 一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return schema 名称 * @return schema 名称
*/ */
String targetSchema() default ""; String targetSchema() default "";
/** /**
* <p> * 目标实体类对应的表一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表名
*
* <p>
* 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时
* 需要在这里指定对应表的表名一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return 表名 * @return 表名
*/ */
@ -67,6 +55,27 @@ public @interface RelationManyToOne {
*/ */
String targetField() default ""; String targetField() default "";
/**
* 中间表名称一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 中间表名称
*/
String joinTable() default "";
/**
* 中间表与当前表的关联字段一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 字段名称列名
*/
String joinSelfColumn() default "";
/**
* 目标表的关联字段名称一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 字段名称和表
*/
String joinTargetColumn() default "";
/** /**
* 默认使用哪个数据源若系统找不到该指定的数据源时默认使用第一个数据源 * 默认使用哪个数据源若系统找不到该指定的数据源时默认使用第一个数据源
* *

View File

@ -35,26 +35,14 @@ public @interface RelationOneToMany {
String selfField() default ""; String selfField() default "";
/** /**
* <p> * 目标实体类对应的表的 schema一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表的 schema 模式
*
* <p>
* 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema
* 需要在这里指定对应表的 schema 一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return schema 名称 * @return schema 名称
*/ */
String targetSchema() default ""; String targetSchema() default "";
/** /**
* <p> * 目标实体类对应的表一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表名
*
* <p>
* 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时
* 需要在这里指定对应表的表名一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return 表名 * @return 表名
*/ */
@ -67,6 +55,32 @@ public @interface RelationOneToMany {
*/ */
String targetField(); String targetField();
/**
* 中间表名称一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 中间表名称
*/
String joinTable() default "";
/**
* 中间表与当前表的关联字段一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 字段名称列名
*/
String joinSelfColumn() default "";
/**
* 目标表的关联字段名称一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 字段名称和表
*/
String joinTargetColumn() default "";
/**
* 查询时追加的额外条件
*/
Condition[] extraConditions() default {};
/** /**
* 查询排序 * 查询排序
* *

View File

@ -35,26 +35,14 @@ public @interface RelationOneToOne {
String selfField() default ""; String selfField() default "";
/** /**
* <p> * 目标实体类对应的表的 schema一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表的 schema 模式
*
* <p>
* 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema
* 需要在这里指定对应表的 schema 一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return schema 名称 * @return schema 名称
*/ */
String targetSchema() default ""; String targetSchema() default "";
/** /**
* <p> * 目标实体类对应的表一般情况下关联数据不是 entity而是 vodto 等需要配置此项
* 目标实体类对应的表名
*
* <p>
* 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时
* 需要在这里指定对应表的表名一般关联数据不是 entity 对象而是 vodto
* 等需要配置此项
* *
* @return 表名 * @return 表名
*/ */
@ -67,6 +55,27 @@ public @interface RelationOneToOne {
*/ */
String targetField(); String targetField();
/**
* 中间表名称一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 中间表名称
*/
String joinTable() default "";
/**
* 中间表与当前表的关联字段一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 字段名称列名
*/
String joinSelfColumn() default "";
/**
* 目标表的关联字段名称一对一的关系是通过通过中间表维护时需要添加此项配置
*
* @return 字段名称和表
*/
String joinTargetColumn() default "";
/** /**
* 默认使用哪个数据源若系统找不到该指定的数据源时默认使用第一个数据源 * 默认使用哪个数据源若系统找不到该指定的数据源时默认使用第一个数据源
* *

View File

@ -15,9 +15,9 @@
*/ */
package com.mybatisflex.core.relation; package com.mybatisflex.core.relation;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.exception.FlexExceptions; import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.table.IdInfo; import com.mybatisflex.core.table.IdInfo;
import com.mybatisflex.core.table.TableInfo; import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory; import com.mybatisflex.core.table.TableInfoFactory;
@ -26,10 +26,9 @@ import com.mybatisflex.core.util.FieldWrapper;
import com.mybatisflex.core.util.StringUtil; import com.mybatisflex.core.util.StringUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Collections; import java.util.*;
import java.util.LinkedHashSet;
import java.util.List; import static com.mybatisflex.core.query.QueryMethods.column;
import java.util.Set;
abstract class AbstractRelation<SelfEntity> { abstract class AbstractRelation<SelfEntity> {
@ -47,14 +46,26 @@ abstract class AbstractRelation<SelfEntity> {
protected TableInfo targetTableInfo; protected TableInfo targetTableInfo;
protected FieldWrapper targetFieldWrapper; protected FieldWrapper targetFieldWrapper;
protected String joinTable;
protected String joinSelfColumn;
protected String joinTargetColumn;
protected String dataSource; protected String dataSource;
protected List<Condition> extraConditions;
public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField, public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField,
String dataSource, Class<SelfEntity> entityClass, Field relationField) { String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> entityClass, Field relationField,
List<Condition> extraConditions
) {
this.selfEntityClass = entityClass; this.selfEntityClass = entityClass;
this.relationField = relationField; this.relationField = relationField;
this.relationFieldWrapper = FieldWrapper.of(entityClass, relationField.getName()); this.relationFieldWrapper = FieldWrapper.of(entityClass, relationField.getName());
this.joinTable = joinTable;
this.joinSelfColumn = joinSelfColumn;
this.joinTargetColumn = joinTargetColumn;
this.dataSource = dataSource; this.dataSource = dataSource;
this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equals(selfField)); this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equals(selfField));
@ -69,6 +80,7 @@ abstract class AbstractRelation<SelfEntity> {
this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField); this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField);
this.targetTableInfo = TableInfoFactory.ofEntityClass(targetEntityClass); this.targetTableInfo = TableInfoFactory.ofEntityClass(targetEntityClass);
this.extraConditions = extraConditions;
} }
@ -144,13 +156,52 @@ abstract class AbstractRelation<SelfEntity> {
this.targetFieldWrapper = targetFieldWrapper; this.targetFieldWrapper = targetFieldWrapper;
} }
protected Set<Object> getSelfFieldValues(List<SelfEntity> list) { public String getTargetSchema() {
if (list == null || list.isEmpty()) { 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 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<Object> getSelfFieldValues(List<SelfEntity> selfEntities) {
if (selfEntities == null || selfEntities.isEmpty()) {
return Collections.emptySet(); return Collections.emptySet();
} }
Set<Object> values = new LinkedHashSet<>(); Set<Object> values = new LinkedHashSet<>();
list.forEach(self -> { selfEntities.forEach(self -> {
Object value = selfFieldWrapper.get(self); Object value = selfFieldWrapper.get(self);
if (value != null && !"".equals(value)) { if (value != null && !"".equals(value)) {
values.add(value); values.add(value);
@ -159,6 +210,13 @@ abstract class AbstractRelation<SelfEntity> {
return values; return values;
} }
public List<Condition> getExtraConditions() {
return extraConditions;
}
public void setExtraConditions(List<Condition> extraConditions) {
this.extraConditions = extraConditions;
}
public Class<?> getMappingType() { public Class<?> getMappingType() {
return relationFieldWrapper.getMappingType(); return relationFieldWrapper.getMappingType();
@ -180,6 +238,10 @@ abstract class AbstractRelation<SelfEntity> {
} }
} }
protected boolean isRelationByMiddleTable() {
return StringUtil.isNotBlank(joinTable);
}
protected static Class<?> getTargetEntityClass(Class<?> entityClass, Field relationField) { protected static Class<?> getTargetEntityClass(Class<?> entityClass, Field relationField) {
return FieldWrapper.of(entityClass, relationField.getName()).getMappingType(); return FieldWrapper.of(entityClass, relationField.getName()).getMappingType();
@ -199,23 +261,62 @@ abstract class AbstractRelation<SelfEntity> {
return primaryKeyList.get(0).getProperty(); return primaryKeyList.get(0).getProperty();
} }
protected static List<Condition> buildConditions(com.mybatisflex.annotation.Condition[] conditions){
if (conditions == null || conditions.length == 0){
return null;
}
List<Condition> conditionList = new ArrayList<>();
for (com.mybatisflex.annotation.Condition condition : conditions) {
conditionList.add(new Condition(condition));
}
return conditionList;
}
/** /**
* Relations 的配置转换为查询的 QueryWrapper * 构建查询目标对象 QueryWrapper
* *
* @param selfEntities 当前的实体类 * @param targetValues 条件的值
* @return QueryWrapper * @return QueryWrapper
*/ */
public abstract QueryWrapper toQueryWrapper(List<SelfEntity> selfEntities); public QueryWrapper buildQueryWrapper(Set<Object> targetValues) {
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(getTargetTableWithSchema());
if (targetValues.size() > 1) {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).in(targetValues));
} else {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).eq(targetValues.iterator().next()));
}
if (extraConditions != null) {
for (Condition extraCondition : extraConditions) {
queryWrapper.and(extraCondition.toQueryCondition());
}
}
customizeQueryWrapper(queryWrapper);
return queryWrapper;
}
/** /**
* 通过 {@link AbstractRelation#toQueryWrapper(List)} 查询到的结果通过此方法进行内存 join * 方便子类最近自定义的条件
* *
* @param queryWrapper 查询条件
*/
public void customizeQueryWrapper(QueryWrapper queryWrapper) {
//do thing
}
/**
* @param selfEntities 当前的实体类列表 * @param selfEntities 当前的实体类列表
* @param targetObjectList 查询到的结果 * @param targetObjectList 查询到的结果
* @param mapper 查询的 Mapper * @param mappingRows 中间表的映射数据非中间表查询的场景下mappingRows 永远为 null
* @param queriedClasses
*/ */
public abstract void join(List<SelfEntity> selfEntities, List<?> targetObjectList, BaseMapper<?> mapper, Set<Class<?>> queriedClasses); public abstract void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows);
} }

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.query.QueryCondition;
import static com.mybatisflex.core.query.QueryMethods.column;
class Condition {
private String column;
private String logic;
private String[] value;
public Condition(com.mybatisflex.annotation.Condition annotation) {
this.column = annotation.column();
this.logic = " " + annotation.logic().toUpperCase().trim()+" ";
this.value = annotation.value();
}
public QueryCondition toQueryCondition() {
return QueryCondition.create(column(column), logic, getValue());
}
public Object getValue() {
if (value == null || value.length == 0) {
return null;
} else if (value.length == 1) {
return value[0];
}
return value;
}
}

View File

@ -16,24 +16,12 @@
package com.mybatisflex.core.relation; package com.mybatisflex.core.relation;
import com.mybatisflex.annotation.RelationManyToMany; import com.mybatisflex.annotation.RelationManyToMany;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.CollectionUtil;
import com.mybatisflex.core.util.MapperUtil;
import com.mybatisflex.core.util.StringUtil; import com.mybatisflex.core.util.StringUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.*;
import static com.mybatisflex.core.query.QueryMethods.column; class ManyToMany<SelfEntity> extends ToManyRelation<SelfEntity> {
class ManyToMany<SelfEntity> extends AbstractRelation<SelfEntity> {
private String joinTable;
private String joinSelfColumn;
private String joinTargetColumn;
private String orderBy; private String orderBy;
@ -42,100 +30,20 @@ class ManyToMany<SelfEntity> extends AbstractRelation<SelfEntity> {
, annotation.targetSchema() , annotation.targetSchema()
, annotation.targetTable() , annotation.targetTable()
, getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField), "@RelationManyToMany.targetField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"") , getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField), "@RelationManyToMany.targetField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"")
, annotation.dataSource(), entityClass, relationField); , annotation.joinTable()
, annotation.joinSelfColumn()
this.joinTable = annotation.joinTable(); , annotation.joinTargetColumn()
this.joinSelfColumn = annotation.joinSelfColumn(); , annotation.dataSource(), entityClass, relationField
this.joinTargetColumn = annotation.joinTargetColumn(); , buildConditions(annotation.extraConditions()));
this.orderBy = annotation.orderBy(); this.orderBy = annotation.orderBy();
} }
@Override
public Class<?> getMappingType() {
return Row.class;
}
@Override @Override
public QueryWrapper toQueryWrapper(List<SelfEntity> selfEntities) { public void customizeQueryWrapper(QueryWrapper queryWrapper) {
Set<Object> selfFieldValues = getSelfFieldValues(selfEntities);
if (selfFieldValues.isEmpty()) {
return null;
}
QueryWrapper queryWrapper = QueryWrapper.create().select()
.from(joinTable);
if (selfFieldValues.size() > 1) {
queryWrapper.where(column(joinSelfColumn).in(selfFieldValues));
} else {
queryWrapper.where(column(joinSelfColumn).eq(selfFieldValues.iterator().next()));
}
return queryWrapper;
}
@Override
public void join(List<SelfEntity> selfEntities, List<?> mappingObjectList, BaseMapper<?> mapper, Set<Class<?>> queriedClasses) {
List<Row> mappingRows = (List<Row>) mappingObjectList;
Set<Object> targetValues = new LinkedHashSet<>();
for (Row row : mappingRows) {
Object targetValue = row.getIgnoreCase(joinTargetColumn);
if (targetValue != null) {
targetValues.add(targetValue);
}
}
if (targetValues.isEmpty()) {
return;
}
QueryWrapper queryWrapper = QueryWrapper.create().select()
.from(getTargetTableWithSchema());
if (targetValues.size() > 1) {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).in(targetValues));
} else {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).eq(targetValues.iterator().next()));
}
if (StringUtil.isNotBlank(orderBy)) { if (StringUtil.isNotBlank(orderBy)) {
queryWrapper.orderBy(orderBy); queryWrapper.orderBy(orderBy);
} }
List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relationFieldWrapper.getMappingType());
RelationManager.doQueryRelations(mapper, targetObjectList, queriedClasses);
if (CollectionUtil.isNotEmpty(targetObjectList)) {
selfEntities.forEach(selfEntity -> {
Object selfValue = selfFieldWrapper.get(selfEntity);
if (selfValue != null) {
selfValue = selfValue.toString();
Set<String> targetMappingValues = new HashSet<>();
for (Row mappingRow : mappingRows) {
if (selfValue.equals(String.valueOf(mappingRow.getIgnoreCase(joinSelfColumn)))) {
Object joinValue = mappingRow.getIgnoreCase(joinTargetColumn);
if (joinValue != null) {
targetMappingValues.add(joinValue.toString());
}
}
}
if (!targetMappingValues.isEmpty()) {
Class<?> wrapType = MapperUtil.getWrapType(relationFieldWrapper.getFieldType());
Collection<Object> collection = (Collection) ClassUtil.newInstance(wrapType);
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValues.contains(targetValue.toString())) {
collection.add(targetObject);
}
}
relationFieldWrapper.set(collection, selfEntity);
}
}
});
}
} }
} }

View File

@ -27,6 +27,9 @@ class ManyToOne<SelfEntity> extends ToOneRelation<SelfEntity> {
, annotation.targetTable() , annotation.targetTable()
, getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField) , getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField)
, "@RelationManyToOne.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"") , "@RelationManyToOne.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"")
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()
, annotation.dataSource() , annotation.dataSource()
, entityClass , entityClass
, relationField); , relationField);

View File

@ -16,76 +16,41 @@
package com.mybatisflex.core.relation; package com.mybatisflex.core.relation;
import com.mybatisflex.annotation.RelationOneToMany; import com.mybatisflex.annotation.RelationOneToMany;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.MapperUtil;
import com.mybatisflex.core.util.StringUtil; import com.mybatisflex.core.util.StringUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.mybatisflex.core.query.QueryMethods.column; class OneToMany<SelfEntity> extends ToManyRelation<SelfEntity> {
class OneToMany<SelfEntity> extends AbstractRelation<SelfEntity> { private String orderBy;
private int limit;
private String orderBy;
private int limit;
public OneToMany(RelationOneToMany annotation, Class<SelfEntity> entityClass, Field relationField) { public OneToMany(RelationOneToMany annotation, Class<SelfEntity> entityClass, Field relationField) {
super(getDefaultPrimaryProperty(annotation.selfField(), entityClass, "@RelationOneToMany.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"") super(getDefaultPrimaryProperty(annotation.selfField(), entityClass, "@RelationOneToMany.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"")
, annotation.targetSchema() , annotation.targetSchema()
, annotation.targetTable() , annotation.targetTable()
, annotation.targetField(), annotation.dataSource(), entityClass, relationField); , annotation.targetField()
this.orderBy = annotation.orderBy(); , annotation.joinTable()
this.limit = annotation.limit(); , annotation.joinSelfColumn()
} , annotation.joinTargetColumn()
, annotation.dataSource(), entityClass, relationField
@Override , buildConditions(annotation.extraConditions()));
public QueryWrapper toQueryWrapper(List<SelfEntity> selfEntities) { this.orderBy = annotation.orderBy();
Set<Object> selfFieldValues = getSelfFieldValues(selfEntities); this.limit = annotation.limit();
if (selfFieldValues.isEmpty()) { }
return null;
}
QueryWrapper queryWrapper = QueryWrapper.create().select()
.from(getTargetTableWithSchema());
if (selfFieldValues.size() > 1) {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).in(selfFieldValues));
} else {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).eq(selfFieldValues.iterator().next()));
}
if (StringUtil.isNotBlank(orderBy)) {
queryWrapper.orderBy(orderBy);
}
if (limit > 0) {
queryWrapper.limit(limit);
}
return queryWrapper;
}
@Override @Override
public void join(List<SelfEntity> selfEntities, List<?> targetObjectList, BaseMapper<?> mapper, Set<Class<?>> queriedClasses) { public void customizeQueryWrapper(QueryWrapper queryWrapper) {
selfEntities.forEach(selfEntity -> { if (StringUtil.isNotBlank(orderBy)) {
Object selfValue = selfFieldWrapper.get(selfEntity); queryWrapper.orderBy(orderBy);
if (selfValue != null) { }
selfValue = selfValue.toString();
Class<?> wrapType = MapperUtil.getWrapType(relationFieldWrapper.getFieldType()); if (limit > 0) {
Collection<Object> collection = (Collection) ClassUtil.newInstance(wrapType); queryWrapper.limit(limit);
for (Object targetObject : targetObjectList) { }
Object targetValue = targetFieldWrapper.get(targetObject); }
if (targetValue != null && selfValue.equals(targetValue.toString())) {
collection.add(targetObject);
}
}
relationFieldWrapper.set(collection, selfEntity);
}
});
}
} }

View File

@ -27,6 +27,9 @@ class OneToOne<SelfEntity> extends ToOneRelation<SelfEntity> {
, annotation.targetSchema() , annotation.targetSchema()
, annotation.targetTable() , annotation.targetTable()
, annotation.targetField() , annotation.targetField()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()
, annotation.dataSource() , annotation.dataSource()
, entityClass , entityClass
, relationField); , relationField);

View File

@ -32,6 +32,8 @@ import java.lang.reflect.Field;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static com.mybatisflex.core.query.QueryMethods.column;
/** /**
* @author michael * @author michael
*/ */
@ -98,8 +100,47 @@ public class RelationManager {
try { try {
relations.forEach(relation -> { relations.forEach(relation -> {
QueryWrapper queryWrapper = relation.toQueryWrapper(entities); Class mappingType = relation.getMappingType();
Class<?> mappingType = relation.getMappingType(); if (queriedClasses.contains(mappingType)) {
return;
}
Set<Object> targetValues;
List<Row> mappingRows = null;
//通过中间表关联查询
if (relation.isRelationByMiddleTable()) {
targetValues = new HashSet<>();
Set selfFieldValues = relation.getSelfFieldValues(entities);
QueryWrapper queryWrapper = QueryWrapper.create().select()
.from(relation.getJoinTable());
if (selfFieldValues.size() > 1) {
queryWrapper.where(column(relation.getJoinSelfColumn()).in(selfFieldValues));
} else {
queryWrapper.where(column(relation.getJoinTargetColumn()).eq(selfFieldValues.iterator().next()));
}
mappingRows = mapper.selectListByQueryAs(queryWrapper, Row.class);
if (CollectionUtil.isEmpty(mappingRows)) {
return;
}
for (Row mappingData : mappingRows) {
Object targetValue = mappingData.getIgnoreCase(relation.getJoinTargetColumn());
if (targetValue != null) {
targetValues.add(targetValue);
}
}
}
//通过外键字段关联查询
else {
targetValues = relation.getSelfFieldValues(entities);
}
if (CollectionUtil.isEmpty(targetValues)) {
return;
}
String dataSource = relation.getDataSource(); String dataSource = relation.getDataSource();
if (StringUtil.isBlank(dataSource) && currentDsKey != null) { if (StringUtil.isBlank(dataSource) && currentDsKey != null) {
@ -111,13 +152,11 @@ public class RelationManager {
DataSourceKey.use(dataSource); DataSourceKey.use(dataSource);
} }
QueryWrapper queryWrapper = relation.buildQueryWrapper(targetValues);
List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, mappingType); List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, mappingType);
if (mappingType != Row.class) {
doQueryRelations(mapper, targetObjectList, queriedClasses);
}
if (CollectionUtil.isNotEmpty(targetObjectList)) { if (CollectionUtil.isNotEmpty(targetObjectList)) {
relation.join(entities, targetObjectList, mapper, queriedClasses); doQueryRelations(mapper, targetObjectList, queriedClasses);
relation.join(entities, targetObjectList, mappingRows);
} }
} finally { } finally {
if (StringUtil.isNotBlank(dataSource)) { if (StringUtil.isNotBlank(dataSource)) {

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.row.Row;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.MapperUtil;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
public ToManyRelation(String selfField, String targetSchema, String targetTable, String targetField,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> selfEntityClass, Field relationField,
List<Condition> extraConditions) {
super(selfField, targetSchema, targetTable, targetField,
joinTable, joinSelfColumn, joinTargetColumn,
dataSource, selfEntityClass, relationField,
extraConditions
);
}
@SuppressWarnings("rawtypes")
@Override
public void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows) {
selfEntities.forEach(selfEntity -> {
Object selfValue = selfFieldWrapper.get(selfEntity);
if (selfValue != null) {
selfValue = selfValue.toString();
Set<String> targetMappingValues = new HashSet<>();
if (mappingRows != null) {
for (Row mappingRow : mappingRows) {
if (selfValue.equals(String.valueOf(mappingRow.getIgnoreCase(joinSelfColumn)))) {
Object joinValue = mappingRow.getIgnoreCase(joinTargetColumn);
if (joinValue != null) {
targetMappingValues.add(joinValue.toString());
}
}
}
} else {
targetMappingValues.add((String) selfValue);
}
if (targetMappingValues.isEmpty()) {
return;
}
Class<?> wrapType = MapperUtil.getWrapType(relationFieldWrapper.getFieldType());
Collection collection = (Collection) ClassUtil.newInstance(wrapType);
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValues.contains(targetValue.toString())) {
collection.add(targetObject);
}
}
relationFieldWrapper.set(collection, selfEntity);
}
});
}
}

View File

@ -15,50 +15,44 @@
*/ */
package com.mybatisflex.core.relation; package com.mybatisflex.core.relation;
import com.mybatisflex.core.BaseMapper; import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.query.QueryWrapper;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List; import java.util.List;
import java.util.Set;
import static com.mybatisflex.core.query.QueryMethods.column;
class ToOneRelation<SelfEntity> extends AbstractRelation<SelfEntity> { class ToOneRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
public ToOneRelation(String selfField, String targetSchema, String targetTable, String targetField, public ToOneRelation(String selfField, String targetSchema, String targetTable, String targetField,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> selfEntityClass, Field relationField) { String dataSource, Class<SelfEntity> selfEntityClass, Field relationField) {
super(selfField, targetSchema, targetTable, targetField, dataSource, selfEntityClass, relationField); super(selfField, targetSchema, targetTable, targetField,
joinTable, joinSelfColumn, joinTargetColumn,
dataSource, selfEntityClass, relationField,
null
);
} }
@Override @Override
public QueryWrapper toQueryWrapper(List<SelfEntity> selfEntities) { public void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows) {
Set<Object> selfFieldValues = getSelfFieldValues(selfEntities);
if (selfFieldValues.isEmpty()) {
return null;
}
QueryWrapper queryWrapper = QueryWrapper.create().select()
.from(getTargetTableWithSchema());
if (selfFieldValues.size() > 1) {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).in(selfFieldValues));
} else {
queryWrapper.where(column(targetTableInfo.getColumnByProperty(targetField.getName())).eq(selfFieldValues.iterator().next()));
}
return queryWrapper;
}
@Override
public void join(List<SelfEntity> selfEntities, List<?> targetObjectList, BaseMapper<?> mapper, Set<Class<?>> queriedClasses) {
selfEntities.forEach(selfEntity -> { selfEntities.forEach(selfEntity -> {
Object selfValue = selfFieldWrapper.get(selfEntity); Object selfValue = selfFieldWrapper.get(selfEntity);
if (selfValue != null) { if (selfValue != null) {
selfValue = selfValue.toString(); selfValue = selfValue.toString();
String targetMappingValue = null;
if (mappingRows != null) {
targetMappingValue = getTargetMappingValue(mappingRows, selfValue);
if (targetMappingValue == null) {
return;
}
} else {
targetMappingValue = (String) selfValue;
}
for (Object targetObject : targetObjectList) { for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject); Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && selfValue.equals(targetValue.toString())) { if (targetValue != null && targetMappingValue.equals(targetValue.toString())) {
relationFieldWrapper.set(targetObject, selfEntity); relationFieldWrapper.set(targetObject, selfEntity);
break; break;
} }
@ -66,4 +60,17 @@ class ToOneRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
} }
}); });
} }
private String getTargetMappingValue(List<Row> mappingRows, Object selfValue) {
for (Row mappingRow : mappingRows) {
if (selfValue.equals(String.valueOf(mappingRow.getIgnoreCase(joinSelfColumn)))) {
Object joinValue = mappingRow.getIgnoreCase(joinTargetColumn);
if (joinValue != null) {
return joinValue.toString();
}
}
}
return null;
}
} }

View File

@ -33,6 +33,8 @@ public class Account implements Serializable {
// @RelationOneToOne(selfField = "id", targetField = "accountId") // @RelationOneToOne(selfField = "id", targetField = "accountId")
// @RelationOneToOne(targetField = "accountId") // @RelationOneToOne(targetField = "accountId")
// @RelationManyToOne(joinTable = "tb_idcard_mapping",joinSelfColumn = "account_id",joinTargetColumn = "idcard_id"
// ,selfField = "id",targetField = "accountId")
private IDCard idCard; private IDCard idCard;
// @RelationOneToMany(selfField = "id", targetField = "accountId") // @RelationOneToMany(selfField = "id", targetField = "accountId")
@ -45,9 +47,12 @@ public class Account implements Serializable {
// targetField = "id", joinTargetColumn = "role_id" // targetField = "id", joinTargetColumn = "role_id"
// ) // )
@RelationManyToMany( @RelationManyToMany(
joinTable = "tb_role_mapping", joinTable = "tb_role_mapping",
joinSelfColumn = "account_id", joinSelfColumn = "account_id",
joinTargetColumn = "role_id" joinTargetColumn = "role_id",
extraConditions = {
@Condition(column = "name", logic = "is not null"),
}
) )
private List<Role> roles; private List<Role> roles;
@ -103,12 +108,12 @@ public class Account implements Serializable {
@Override @Override
public String toString() { public String toString() {
return "Account{" + return "Account{" +
"id=" + id + "id=" + id +
", userName='" + userName + '\'' + ", userName='" + userName + '\'' +
", age=" + age + ", age=" + age +
", idCard=" + idCard + ", idCard=" + idCard +
", books=" + books + ", books=" + books +
", roles=" + roles + ", roles=" + roles +
'}'; '}';
} }
} }

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.test.relation.onetoone;
import com.mybatisflex.annotation.RelationManyToMany;
import java.io.Serializable;
import java.util.List;
public class AccountDTO implements Serializable {
private Long id;
private String userName;
private int age;
// @RelationOneToOne(selfField = "id", targetField = "accountId")
// @RelationOneToOne(targetField = "accountId")
private IDCard idCard;
// @RelationOneToMany(selfField = "id", targetField = "accountId")
// @RelationOneToMany(targetField = "accountId")
private List<Book> books;
// @RelationManyToMany(
// joinTable = "tb_role_mapping",
// selfField = "id", joinSelfColumn = "account_id",
// targetField = "id", joinTargetColumn = "role_id"
// )
@RelationManyToMany(
joinTable = "tb_role_mapping",
joinSelfColumn = "account_id",
joinTargetColumn = "role_id"
)
private List<Role> roles;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public IDCard getIdCard() {
return idCard;
}
public void setIdCard(IDCard idCard) {
this.idCard = idCard;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "AccountDTO{" +
"id=" + id +
", userName='" + userName + '\'' +
", age=" + age +
", idCard=" + idCard +
", books=" + books +
", roles=" + roles +
'}';
}
}

View File

@ -13,6 +13,16 @@ VALUES (1,'0001', '内容1'),
(4,'0004', '内容4'), (4,'0004', '内容4'),
(5,'0005', '内容5'); (5,'0005', '内容5');
INSERT INTO tb_idcard_mapping
VALUES (1,1),
(2,2),
(3,3),
(4,4),
(5,5);
INSERT INTO tb_book INSERT INTO tb_book
VALUES (1,1,'图书1', '内容1'), VALUES (1,1,'图书1', '内容1'),
(2,2,'图书2', '内容2'), (2,2,'图书2', '内容2'),
@ -49,4 +59,4 @@ VALUES (1,0,'顶级菜单1'),
(10,5,'子菜单'), (10,5,'子菜单'),
(10,5,'子菜单'), (10,5,'子菜单'),
(10,9,'子菜单'), (10,9,'子菜单'),
(10,9,'子菜单'); (10,9,'子菜单');

View File

@ -14,6 +14,15 @@ CREATE TABLE IF NOT EXISTS `tb_idcard`
); );
CREATE TABLE IF NOT EXISTS `tb_idcard_mapping`
(
`account_id` Integer,
`idcard_id` Integer
);
CREATE TABLE IF NOT EXISTS `tb_book` CREATE TABLE IF NOT EXISTS `tb_book`
( (
`id` INTEGER auto_increment, `id` INTEGER auto_increment,
@ -39,4 +48,4 @@ CREATE TABLE IF NOT EXISTS `tb_menu`
`id` INTEGER auto_increment, `id` INTEGER auto_increment,
`parent_id` INTEGER, `parent_id` INTEGER,
`name` VARCHAR(100) `name` VARCHAR(100)
); );