diff --git a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/Condition.java b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/Condition.java new file mode 100644 index 00000000..9ba6eb42 --- /dev/null +++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/Condition.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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.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 {}; + +} diff --git a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToMany.java b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToMany.java index 915238a9..24427249 100644 --- a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToMany.java +++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToMany.java @@ -35,26 +35,14 @@ public @interface RelationManyToMany { String selfField() default ""; /** - *

- * 目标实体类对应的表的 schema 模式。 - * - *

- * 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema 时, - * 需要在这里指定对应表的 schema 值。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表的 schema,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return schema 名称 */ String targetSchema() default ""; /** - *

- * 目标实体类对应的表名。 - * - *

- * 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时, - * 需要在这里指定对应表的表名。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return 表名 */ @@ -88,6 +76,11 @@ public @interface RelationManyToMany { */ String joinTargetColumn(); + /** + * 查询时,追加的额外条件 + */ + Condition[] extraConditions() default {}; + /** * 查询排序。 * @@ -102,4 +95,5 @@ public @interface RelationManyToMany { */ String dataSource() default ""; + } diff --git a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToOne.java b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToOne.java index 22750e4d..6c406f34 100644 --- a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToOne.java +++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToOne.java @@ -35,26 +35,14 @@ public @interface RelationManyToOne { String selfField(); /** - *

- * 目标实体类对应的表的 schema 模式。 - * - *

- * 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema 时, - * 需要在这里指定对应表的 schema 值。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表的 schema,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return schema 名称 */ String targetSchema() default ""; /** - *

- * 目标实体类对应的表名。 - * - *

- * 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时, - * 需要在这里指定对应表的表名。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return 表名 */ @@ -67,6 +55,27 @@ public @interface RelationManyToOne { */ String targetField() default ""; + /** + * 中间表名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 中间表名称 + */ + String joinTable() default ""; + + /** + * 中间表与当前表的关联字段,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 字段名称,列名 + */ + String joinSelfColumn() default ""; + + /** + * 目标表的关联字段名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 字段名称和表 + */ + String joinTargetColumn() default ""; + /** * 默认使用哪个数据源,若系统找不到该指定的数据源时,默认使用第一个数据源。 * diff --git a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToMany.java b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToMany.java index 85f5fac4..6fec3ec7 100644 --- a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToMany.java +++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToMany.java @@ -35,26 +35,14 @@ public @interface RelationOneToMany { String selfField() default ""; /** - *

- * 目标实体类对应的表的 schema 模式。 - * - *

- * 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema 时, - * 需要在这里指定对应表的 schema 值。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表的 schema,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return schema 名称 */ String targetSchema() default ""; /** - *

- * 目标实体类对应的表名。 - * - *

- * 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时, - * 需要在这里指定对应表的表名。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return 表名 */ @@ -67,6 +55,32 @@ public @interface RelationOneToMany { */ String targetField(); + /** + * 中间表名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 中间表名称 + */ + String joinTable() default ""; + + /** + * 中间表与当前表的关联字段,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 字段名称,列名 + */ + String joinSelfColumn() default ""; + + /** + * 目标表的关联字段名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 字段名称和表 + */ + String joinTargetColumn() default ""; + + /** + * 查询时,追加的额外条件 + */ + Condition[] extraConditions() default {}; + /** * 查询排序。 * diff --git a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToOne.java b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToOne.java index ab738c85..2d5a5dd3 100644 --- a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToOne.java +++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToOne.java @@ -35,26 +35,14 @@ public @interface RelationOneToOne { String selfField() default ""; /** - *

- * 目标实体类对应的表的 schema 模式。 - * - *

- * 如果目标实体类没有使用 {@code @Table(schema = "...")} 指定 schema 时, - * 需要在这里指定对应表的 schema 值。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表的 schema,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return schema 名称 */ String targetSchema() default ""; /** - *

- * 目标实体类对应的表名。 - * - *

- * 如果目标实体类没有使用 {@code @Table(value = "...")} 指定表名时, - * 需要在这里指定对应表的表名。一般关联数据不是 entity 对象,而是 vo、dto - * 等需要配置此项。 + * 目标实体类对应的表,一般情况下,关联数据不是 entity,而是 vo、dto 等需要配置此项 * * @return 表名 */ @@ -67,6 +55,27 @@ public @interface RelationOneToOne { */ String targetField(); + /** + * 中间表名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 中间表名称 + */ + String joinTable() default ""; + + /** + * 中间表与当前表的关联字段,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 字段名称,列名 + */ + String joinSelfColumn() default ""; + + /** + * 目标表的关联字段名称,一对一的关系是通过通过中间表维护时,需要添加此项配置。 + * + * @return 字段名称和表 + */ + String joinTargetColumn() default ""; + /** * 默认使用哪个数据源,若系统找不到该指定的数据源时,默认使用第一个数据源。 * diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/AbstractRelation.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/AbstractRelation.java index 5988d572..5b3f2b5d 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/AbstractRelation.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/AbstractRelation.java @@ -15,9 +15,9 @@ */ package com.mybatisflex.core.relation; -import com.mybatisflex.core.BaseMapper; import com.mybatisflex.core.exception.FlexExceptions; 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; @@ -26,10 +26,9 @@ import com.mybatisflex.core.util.FieldWrapper; import com.mybatisflex.core.util.StringUtil; import java.lang.reflect.Field; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; +import java.util.*; + +import static com.mybatisflex.core.query.QueryMethods.column; abstract class AbstractRelation { @@ -47,14 +46,26 @@ abstract class AbstractRelation { protected TableInfo targetTableInfo; protected FieldWrapper targetFieldWrapper; + protected String joinTable; + protected String joinSelfColumn; + protected String joinTargetColumn; + protected String dataSource; + protected List extraConditions; public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField, - String dataSource, Class entityClass, Field relationField) { + String joinTable, String joinSelfColumn, String joinTargetColumn, + String dataSource, Class entityClass, Field relationField, + List extraConditions + ) { 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().equals(selfField)); @@ -69,6 +80,7 @@ abstract class AbstractRelation { this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField); this.targetTableInfo = TableInfoFactory.ofEntityClass(targetEntityClass); + this.extraConditions = extraConditions; } @@ -144,13 +156,52 @@ abstract class AbstractRelation { this.targetFieldWrapper = targetFieldWrapper; } - protected Set getSelfFieldValues(List list) { - if (list == null || list.isEmpty()) { + 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 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 getSelfFieldValues(List selfEntities) { + if (selfEntities == null || selfEntities.isEmpty()) { return Collections.emptySet(); } - Set values = new LinkedHashSet<>(); - list.forEach(self -> { + selfEntities.forEach(self -> { Object value = selfFieldWrapper.get(self); if (value != null && !"".equals(value)) { values.add(value); @@ -159,6 +210,13 @@ abstract class AbstractRelation { return values; } + public List getExtraConditions() { + return extraConditions; + } + + public void setExtraConditions(List extraConditions) { + this.extraConditions = extraConditions; + } public Class getMappingType() { return relationFieldWrapper.getMappingType(); @@ -180,6 +238,10 @@ abstract class AbstractRelation { } } + protected boolean isRelationByMiddleTable() { + return StringUtil.isNotBlank(joinTable); + } + protected static Class getTargetEntityClass(Class entityClass, Field relationField) { return FieldWrapper.of(entityClass, relationField.getName()).getMappingType(); @@ -199,23 +261,62 @@ abstract class AbstractRelation { return primaryKeyList.get(0).getProperty(); } + protected static List buildConditions(com.mybatisflex.annotation.Condition[] conditions){ + if (conditions == null || conditions.length == 0){ + return null; + } + List 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 */ - public abstract QueryWrapper toQueryWrapper(List selfEntities); + public QueryWrapper buildQueryWrapper(Set 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 targetObjectList 查询到的结果 - * @param mapper 查询的 Mapper - * @param queriedClasses + * @param mappingRows 中间表的映射数据,非中间表查询的场景下,mappingRows 永远为 null */ - public abstract void join(List selfEntities, List targetObjectList, BaseMapper mapper, Set> queriedClasses); + public abstract void join(List selfEntities, List targetObjectList, List mappingRows); + } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/Condition.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/Condition.java new file mode 100644 index 00000000..f46604a5 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/Condition.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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.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; + } +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToMany.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToMany.java index 75067687..4bcf6ebc 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToMany.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToMany.java @@ -16,24 +16,12 @@ package com.mybatisflex.core.relation; import com.mybatisflex.annotation.RelationManyToMany; -import com.mybatisflex.core.BaseMapper; 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 java.lang.reflect.Field; -import java.util.*; -import static com.mybatisflex.core.query.QueryMethods.column; - -class ManyToMany extends AbstractRelation { - - private String joinTable; - private String joinSelfColumn; - private String joinTargetColumn; +class ManyToMany extends ToManyRelation { private String orderBy; @@ -42,100 +30,20 @@ class ManyToMany extends AbstractRelation { , annotation.targetSchema() , annotation.targetTable() , getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField), "@RelationManyToMany.targetField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"") - , annotation.dataSource(), entityClass, relationField); - - this.joinTable = annotation.joinTable(); - this.joinSelfColumn = annotation.joinSelfColumn(); - this.joinTargetColumn = annotation.joinTargetColumn(); - + , annotation.joinTable() + , annotation.joinSelfColumn() + , annotation.joinTargetColumn() + , annotation.dataSource(), entityClass, relationField + , buildConditions(annotation.extraConditions())); this.orderBy = annotation.orderBy(); } - @Override - public Class getMappingType() { - return Row.class; - } - @Override - public QueryWrapper toQueryWrapper(List selfEntities) { - Set 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 selfEntities, List mappingObjectList, BaseMapper mapper, Set> queriedClasses) { - List mappingRows = (List) mappingObjectList; - Set 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())); - } - + public void customizeQueryWrapper(QueryWrapper queryWrapper) { if (StringUtil.isNotBlank(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 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 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); - } - } - }); - } } + } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToOne.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToOne.java index 89d0ef7e..06e81f57 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToOne.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToOne.java @@ -27,6 +27,9 @@ class ManyToOne extends ToOneRelation { , annotation.targetTable() , getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField) , "@RelationManyToOne.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"") + , annotation.joinTable() + , annotation.joinSelfColumn() + , annotation.joinTargetColumn() , annotation.dataSource() , entityClass , relationField); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToMany.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToMany.java index 800c26e8..68e9aa22 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToMany.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToMany.java @@ -16,76 +16,41 @@ package com.mybatisflex.core.relation; import com.mybatisflex.annotation.RelationOneToMany; -import com.mybatisflex.core.BaseMapper; 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 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 extends ToManyRelation { -class OneToMany extends AbstractRelation { - - private String orderBy; - private int limit; + private String orderBy; + private int limit; - public OneToMany(RelationOneToMany annotation, Class entityClass, Field relationField) { - super(getDefaultPrimaryProperty(annotation.selfField(), entityClass, "@RelationOneToMany.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"") + public OneToMany(RelationOneToMany annotation, Class entityClass, Field relationField) { + super(getDefaultPrimaryProperty(annotation.selfField(), entityClass, "@RelationOneToMany.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"") , annotation.targetSchema() , annotation.targetTable() - , annotation.targetField(), annotation.dataSource(), entityClass, relationField); - this.orderBy = annotation.orderBy(); - this.limit = annotation.limit(); - } - - @Override - public QueryWrapper toQueryWrapper(List selfEntities) { - Set 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())); - } - - if (StringUtil.isNotBlank(orderBy)) { - queryWrapper.orderBy(orderBy); - } - - if (limit > 0) { - queryWrapper.limit(limit); - } - - return queryWrapper; - } + , annotation.targetField() + , annotation.joinTable() + , annotation.joinSelfColumn() + , annotation.joinTargetColumn() + , annotation.dataSource(), entityClass, relationField + , buildConditions(annotation.extraConditions())); + this.orderBy = annotation.orderBy(); + this.limit = annotation.limit(); + } - @Override - public void join(List selfEntities, List targetObjectList, BaseMapper mapper, Set> queriedClasses) { - selfEntities.forEach(selfEntity -> { - Object selfValue = selfFieldWrapper.get(selfEntity); - if (selfValue != null) { - selfValue = selfValue.toString(); - Class wrapType = MapperUtil.getWrapType(relationFieldWrapper.getFieldType()); - Collection collection = (Collection) ClassUtil.newInstance(wrapType); - for (Object targetObject : targetObjectList) { - Object targetValue = targetFieldWrapper.get(targetObject); - if (targetValue != null && selfValue.equals(targetValue.toString())) { - collection.add(targetObject); - } - } - relationFieldWrapper.set(collection, selfEntity); - } - }); - } + @Override + public void customizeQueryWrapper(QueryWrapper queryWrapper) { + if (StringUtil.isNotBlank(orderBy)) { + queryWrapper.orderBy(orderBy); + } + + if (limit > 0) { + queryWrapper.limit(limit); + } + } + } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToOne.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToOne.java index 45bb9a04..267ffa65 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToOne.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToOne.java @@ -27,6 +27,9 @@ class OneToOne extends ToOneRelation { , annotation.targetSchema() , annotation.targetTable() , annotation.targetField() + , annotation.joinTable() + , annotation.joinSelfColumn() + , annotation.joinTargetColumn() , annotation.dataSource() , entityClass , relationField); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/RelationManager.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/RelationManager.java index 4b72535b..81733943 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/RelationManager.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/RelationManager.java @@ -32,6 +32,8 @@ import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import static com.mybatisflex.core.query.QueryMethods.column; + /** * @author michael */ @@ -98,8 +100,47 @@ public class RelationManager { try { relations.forEach(relation -> { - QueryWrapper queryWrapper = relation.toQueryWrapper(entities); - Class mappingType = relation.getMappingType(); + Class mappingType = relation.getMappingType(); + if (queriedClasses.contains(mappingType)) { + return; + } + + Set targetValues; + List 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(); if (StringUtil.isBlank(dataSource) && currentDsKey != null) { @@ -111,13 +152,11 @@ public class RelationManager { DataSourceKey.use(dataSource); } + QueryWrapper queryWrapper = relation.buildQueryWrapper(targetValues); List targetObjectList = mapper.selectListByQueryAs(queryWrapper, mappingType); - if (mappingType != Row.class) { - doQueryRelations(mapper, targetObjectList, queriedClasses); - } - if (CollectionUtil.isNotEmpty(targetObjectList)) { - relation.join(entities, targetObjectList, mapper, queriedClasses); + doQueryRelations(mapper, targetObjectList, queriedClasses); + relation.join(entities, targetObjectList, mappingRows); } } finally { if (StringUtil.isNotBlank(dataSource)) { diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ToManyRelation.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ToManyRelation.java new file mode 100644 index 00000000..b86b51e2 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ToManyRelation.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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.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 extends AbstractRelation { + + + public ToManyRelation(String selfField, String targetSchema, String targetTable, String targetField, + String joinTable, String joinSelfColumn, String joinTargetColumn, + String dataSource, Class selfEntityClass, Field relationField, + List extraConditions) { + super(selfField, targetSchema, targetTable, targetField, + joinTable, joinSelfColumn, joinTargetColumn, + dataSource, selfEntityClass, relationField, + extraConditions + ); + } + + + @SuppressWarnings("rawtypes") + @Override + public void join(List selfEntities, List targetObjectList, List mappingRows) { + selfEntities.forEach(selfEntity -> { + Object selfValue = selfFieldWrapper.get(selfEntity); + if (selfValue != null) { + selfValue = selfValue.toString(); + Set 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); + } + }); + } +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ToOneRelation.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ToOneRelation.java index c356b30e..0eaaa777 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ToOneRelation.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ToOneRelation.java @@ -15,50 +15,44 @@ */ package com.mybatisflex.core.relation; -import com.mybatisflex.core.BaseMapper; -import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.row.Row; import java.lang.reflect.Field; import java.util.List; -import java.util.Set; - -import static com.mybatisflex.core.query.QueryMethods.column; class ToOneRelation extends AbstractRelation { public ToOneRelation(String selfField, String targetSchema, String targetTable, String targetField, + String joinTable, String joinSelfColumn, String joinTargetColumn, String dataSource, Class selfEntityClass, Field relationField) { - super(selfField, targetSchema, targetTable, targetField, dataSource, selfEntityClass, relationField); + super(selfField, targetSchema, targetTable, targetField, + joinTable, joinSelfColumn, joinTargetColumn, + dataSource, selfEntityClass, relationField, + null + ); } @Override - public QueryWrapper toQueryWrapper(List selfEntities) { - Set 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 selfEntities, List targetObjectList, BaseMapper mapper, Set> queriedClasses) { + public void join(List selfEntities, List targetObjectList, List mappingRows) { selfEntities.forEach(selfEntity -> { Object selfValue = selfFieldWrapper.get(selfEntity); if (selfValue != null) { 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) { Object targetValue = targetFieldWrapper.get(targetObject); - if (targetValue != null && selfValue.equals(targetValue.toString())) { + if (targetValue != null && targetMappingValue.equals(targetValue.toString())) { relationFieldWrapper.set(targetObject, selfEntity); break; } @@ -66,4 +60,17 @@ class ToOneRelation extends AbstractRelation { } }); } + + + private String getTargetMappingValue(List 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; + } } diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Account.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Account.java index 35605974..d1c86261 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Account.java +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Account.java @@ -33,6 +33,8 @@ public class Account implements Serializable { // @RelationOneToOne(selfField = "id", targetField = "accountId") // @RelationOneToOne(targetField = "accountId") +// @RelationManyToOne(joinTable = "tb_idcard_mapping",joinSelfColumn = "account_id",joinTargetColumn = "idcard_id" +// ,selfField = "id",targetField = "accountId") private IDCard idCard; // @RelationOneToMany(selfField = "id", targetField = "accountId") @@ -45,9 +47,12 @@ public class Account implements Serializable { // targetField = "id", joinTargetColumn = "role_id" // ) @RelationManyToMany( - joinTable = "tb_role_mapping", - joinSelfColumn = "account_id", - joinTargetColumn = "role_id" + joinTable = "tb_role_mapping", + joinSelfColumn = "account_id", + joinTargetColumn = "role_id", + extraConditions = { + @Condition(column = "name", logic = "is not null"), + } ) private List roles; @@ -103,12 +108,12 @@ public class Account implements Serializable { @Override public String toString() { return "Account{" + - "id=" + id + - ", userName='" + userName + '\'' + - ", age=" + age + - ", idCard=" + idCard + - ", books=" + books + - ", roles=" + roles + - '}'; + "id=" + id + + ", userName='" + userName + '\'' + + ", age=" + age + + ", idCard=" + idCard + + ", books=" + books + + ", roles=" + roles + + '}'; } } diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/AccountDTO.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/AccountDTO.java new file mode 100644 index 00000000..c097953e --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/AccountDTO.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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.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 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 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 getBooks() { + return books; + } + + public void setBooks(List books) { + this.books = books; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + return "AccountDTO{" + + "id=" + id + + ", userName='" + userName + '\'' + + ", age=" + age + + ", idCard=" + idCard + + ", books=" + books + + ", roles=" + roles + + '}'; + } +} diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/data.sql b/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/data.sql index 251108f2..f40561f7 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/data.sql +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/data.sql @@ -13,6 +13,16 @@ VALUES (1,'0001', '内容1'), (4,'0004', '内容4'), (5,'0005', '内容5'); +INSERT INTO tb_idcard_mapping +VALUES (1,1), + (2,2), + (3,3), + (4,4), + (5,5); + + + + INSERT INTO tb_book VALUES (1,1,'图书1', '内容1'), (2,2,'图书2', '内容2'), @@ -49,4 +59,4 @@ VALUES (1,0,'顶级菜单1'), (10,5,'子菜单'), (10,5,'子菜单'), (10,9,'子菜单'), - (10,9,'子菜单'); \ No newline at end of file + (10,9,'子菜单'); diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/schema.sql b/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/schema.sql index 4f054da6..6c68202a 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/schema.sql +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/schema.sql @@ -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` ( `id` INTEGER auto_increment, @@ -39,4 +48,4 @@ CREATE TABLE IF NOT EXISTS `tb_menu` `id` INTEGER auto_increment, `parent_id` INTEGER, `name` VARCHAR(100) -); \ No newline at end of file +);