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
new file mode 100644
index 00000000..e778a2f0
--- /dev/null
+++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToMany.java
@@ -0,0 +1,37 @@
+/*
+ * 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.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface RelationManyToMany {
+
+ String selfField();
+
+ String targetField();
+
+ String joinTable();
+
+ String joinSelfColumn();
+
+ String joinTargetColumn();
+
+ String orderBy() default "";
+
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..afe56708
--- /dev/null
+++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationManyToOne.java
@@ -0,0 +1,29 @@
+/*
+ * 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.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface RelationManyToOne {
+
+ String selfField();
+
+ String targetField();
+
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..983b7f10
--- /dev/null
+++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToMany.java
@@ -0,0 +1,33 @@
+/*
+ * 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.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface RelationOneToMany {
+
+ String selfField();
+
+ String targetField();
+
+ String orderBy() default "";
+
+ int limit() default 0;
+
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..9536ba16
--- /dev/null
+++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/RelationOneToOne.java
@@ -0,0 +1,29 @@
+/*
+ * 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.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface RelationOneToOne {
+
+ String selfField();
+
+ String targetField();
+
+}
\ No newline at end of file
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java
index 83e505ec..06f55fdf 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java
@@ -426,6 +426,58 @@ public interface BaseMapper {
return MapperUtil.getSelectOneResult(selectListByQueryAs(queryWrapper, asType));
}
+
+ /**
+ * 根据 map 构建的条件来查询数据
+ *
+ * @param map where 条件
+ * @return entity 数据
+ */
+ default T selectOneWithRelationsByMap(Map map) {
+ if (map == null || map.isEmpty()) {
+ throw FlexExceptions.wrap("map can not be null or empty.");
+ }
+ return selectOneWithRelationsByQuery(QueryWrapper.create().where(map).limit(1));
+ }
+
+
+ /**
+ * 根据 condition 来查询数据
+ *
+ * @param condition 条件
+ * @return 1 条数据
+ */
+ default T selectOneWithRelationsByCondition(QueryCondition condition) {
+ if (condition == null) {
+ throw FlexExceptions.wrap("condition can not be null.");
+ }
+ return selectOneWithRelationsByQuery(QueryWrapper.create().where(condition).limit(1));
+ }
+
+
+ /**
+ * 根据 queryWrapper 构建的条件来查询 1 条数据
+ *
+ * @param queryWrapper query 条件
+ * @return entity 数据
+ */
+ default T selectOneWithRelationsByQuery(QueryWrapper queryWrapper) {
+ return MapperUtil.queryRelations(this, MapperUtil.getSelectOneResult(selectListByQuery(queryWrapper)));
+ }
+
+
+ /**
+ * 根据 queryWrapper 构建的条件来查询 1 条数据
+ *
+ * @param queryWrapper query 条件
+ * @param asType 接收类型
+ * @return 数据内容
+ */
+ default R selectOneWithRelationsByQueryAs(QueryWrapper queryWrapper, Class asType) {
+ return MapperUtil.queryRelations(this, MapperUtil.getSelectOneResult(selectListByQueryAs(queryWrapper, asType)));
+ }
+
+
/**
* 根据多个主键来查询多条数据
*
@@ -552,7 +604,7 @@ public interface BaseMapper {
return selectObjectListByQueryAs(queryWrapper, asType);
}
- if(Map.class.isAssignableFrom(asType)){
+ if (Map.class.isAssignableFrom(asType)) {
return (List) selectRowsByQuery(queryWrapper);
}
@@ -565,6 +617,14 @@ public interface BaseMapper {
}
+ /**
+ * 根据 query 来构建条件查询数据列表,要求返回的数据为 asType
+ *
+ * @param queryWrapper 查询条件
+ * @param asType 接收的数据类型
+ * @param consumers 字段查询
+ * @return 数据列表
+ */
default List selectListByQueryAs(QueryWrapper queryWrapper, Class asType
, Consumer>... consumers) {
List list = selectListByQueryAs(queryWrapper, asType);
@@ -577,6 +637,63 @@ public interface BaseMapper {
}
+ /**
+ * 查询 entity 及其 relation 注解字段
+ *
+ * @param queryWrapper 查询条件
+ */
+ default List selectListWithRelationsByQuery(QueryWrapper queryWrapper) {
+ return MapperUtil.queryRelations(this, selectListByQuery(queryWrapper));
+ }
+
+
+ /**
+ * 查询 entity 及其 relation 注解字段
+ *
+ * @param queryWrapper 查询条件
+ * @param asType 要求返回的数据类型
+ * @return 数据列表
+ */
+ default List selectListWithRelationsByQueryAs(QueryWrapper queryWrapper, Class asType) {
+ if (Number.class.isAssignableFrom(asType)
+ || String.class == asType) {
+ return selectObjectListByQueryAs(queryWrapper, asType);
+ }
+
+ if (Map.class.isAssignableFrom(asType)) {
+ return (List) selectRowsByQuery(queryWrapper);
+ }
+
+ try {
+ MappedStatementTypes.setCurrentType(asType);
+ return MapperUtil.queryRelations(this, (List) selectListByQuery(queryWrapper));
+ } finally {
+ MappedStatementTypes.clear();
+ }
+ }
+
+
+ /**
+ * 查询 entity 及其 relation 注解字段
+ *
+ * @param queryWrapper 查询条件
+ * @param asType 返回的类型
+ * @param consumers 字段查询
+ * @return 数据列表
+ */
+ default List selectListWithRelationsByQueryAs(QueryWrapper queryWrapper, Class asType
+ , Consumer>... consumers) {
+ List list = selectListByQueryAs(queryWrapper, asType);
+ if (list == null || list.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ MapperUtil.queryRelations(this, list);
+ MapperUtil.queryFields(this, list, consumers);
+ return list;
+ }
+ }
+
+
/**
* 查询全部数据
*
@@ -587,6 +704,16 @@ public interface BaseMapper {
}
+ /**
+ * 查询全部数据,及其 relation 字段内容
+ *
+ * @return 数据列表
+ */
+ default List selectAllWithRelations() {
+ return MapperUtil.queryRelations(this, selectListByQuery(new QueryWrapper()));
+ }
+
+
/**
* 根据 queryWrapper 1 条数据
* queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java
index 42b20d83..d412d023 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java
@@ -151,7 +151,7 @@ public class FlexConfiguration extends Configuration {
Class> asType = MappedStatementTypes.getCurrentType();
if (asType != null) {
return MapUtil.computeIfAbsent(dynamicMappedStatementCache, id + ":" + asType.getName(),
- aClass -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType))
+ clazz -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType))
);
}
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
new file mode 100644
index 00000000..0fda7a53
--- /dev/null
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToMany.java
@@ -0,0 +1,135 @@
+/*
+ * 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.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 Relation {
+
+ private String joinTable;
+ private String joinSelfColumn;
+ private String joinTargetColumn;
+
+ private String orderBy;
+
+ public ManyToMany(RelationManyToMany annotation, Class entityClass, Field relationField) {
+ super(annotation.selfField(), annotation.targetField(), entityClass, relationField);
+
+ this.joinTable = annotation.joinTable();
+ this.joinSelfColumn = annotation.joinSelfColumn();
+ this.joinTargetColumn = annotation.joinTargetColumn();
+
+ 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 map(List selfEntities, List> mappingObjectList, BaseMapper> mapper) {
+ 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(targetTableInfo.getTableNameWithSchema());
+ 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)) {
+ queryWrapper.orderBy(orderBy);
+ }
+
+
+ List> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relationFieldWrapper.getMappingType());
+
+ 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
new file mode 100644
index 00000000..2a872b82
--- /dev/null
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/ManyToOne.java
@@ -0,0 +1,66 @@
+/*
+ * 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.annotation.RelationManyToOne;
+import com.mybatisflex.core.BaseMapper;
+import com.mybatisflex.core.query.QueryWrapper;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Set;
+
+import static com.mybatisflex.core.query.QueryMethods.column;
+
+class ManyToOne extends Relation {
+
+ public ManyToOne(RelationManyToOne annotation, Class entityClass, Field relationField) {
+ super(annotation.selfField(), annotation.targetField(), entityClass, relationField);
+ }
+
+ @Override
+ public QueryWrapper toQueryWrapper(List selfEntities) {
+ Set selfFieldValues = getSelfFieldValues(selfEntities);
+ if (selfFieldValues.isEmpty()) {
+ return null;
+ }
+ QueryWrapper queryWrapper = QueryWrapper.create().select()
+ .from(targetTableInfo.getTableNameWithSchema());
+ 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 map(List selfEntities, List> targetObjectList, BaseMapper> mapper) {
+ selfEntities.forEach(selfEntity -> {
+ Object selfValue = selfFieldWrapper.get(selfEntity);
+ if (selfValue != null) {
+ for (Object targetObject : targetObjectList) {
+ Object targetValue = targetFieldWrapper.get(targetObject);
+ if (selfValue.equals(targetValue)) {
+ relationFieldWrapper.set(targetObject, selfEntity);
+ break;
+ }
+ }
+ }
+ });
+ }
+}
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
new file mode 100644
index 00000000..b9b388ba
--- /dev/null
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToMany.java
@@ -0,0 +1,86 @@
+/*
+ * 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.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 Relation {
+
+ private String orderBy;
+ private int limit;
+
+
+ public OneToMany(RelationOneToMany annotation, Class entityClass, Field relationField) {
+ super(annotation.selfField(), annotation.targetField(), 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(targetTableInfo.getTableNameWithSchema());
+ 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
+ public void map(List selfEntities, List> targetObjectList, BaseMapper> mapper) {
+ selfEntities.forEach(selfEntity -> {
+ Object selfValue = selfFieldWrapper.get(selfEntity);
+ if (selfValue != null) {
+ Class> wrapType = MapperUtil.getWrapType(relationFieldWrapper.getFieldType());
+ Collection collection = (Collection) ClassUtil.newInstance(wrapType);
+ for (Object targetObject : targetObjectList) {
+ Object targetValue = targetFieldWrapper.get(targetObject);
+ if (selfValue.equals(targetValue)) {
+ collection.add(targetObject);
+ }
+ }
+ relationFieldWrapper.set(collection, selfEntity);
+ }
+ });
+ }
+}
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
new file mode 100644
index 00000000..f919a2f1
--- /dev/null
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/OneToOne.java
@@ -0,0 +1,67 @@
+/*
+ * 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.annotation.RelationOneToOne;
+import com.mybatisflex.core.BaseMapper;
+import com.mybatisflex.core.query.QueryWrapper;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Set;
+
+import static com.mybatisflex.core.query.QueryMethods.column;
+
+class OneToOne extends Relation {
+
+ public OneToOne(RelationOneToOne annotation, Class entityClass, Field relationField) {
+ super(annotation.selfField(), annotation.targetField(), entityClass, relationField);
+ }
+
+
+ @Override
+ public QueryWrapper toQueryWrapper(List selfEntities) {
+ Set selfFieldValues = getSelfFieldValues(selfEntities);
+ if (selfFieldValues.isEmpty()) {
+ return null;
+ }
+ QueryWrapper queryWrapper = QueryWrapper.create().select()
+ .from(targetTableInfo.getTableNameWithSchema());
+ 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 map(List selfEntities, List> targetObjectList, BaseMapper> mapper) {
+ selfEntities.forEach(selfEntity -> {
+ Object selfValue = selfFieldWrapper.get(selfEntity);
+ if (selfValue != null) {
+ for (Object targetObject : targetObjectList) {
+ Object targetValue = targetFieldWrapper.get(targetObject);
+ if (selfValue.equals(targetValue)) {
+ relationFieldWrapper.set(targetObject, selfEntity);
+ break;
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/Relation.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/Relation.java
new file mode 100644
index 00000000..68be5e3b
--- /dev/null
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/Relation.java
@@ -0,0 +1,170 @@
+/*
+ * 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.BaseMapper;
+import com.mybatisflex.core.query.QueryWrapper;
+import com.mybatisflex.core.table.TableInfo;
+import com.mybatisflex.core.table.TableInfoFactory;
+import com.mybatisflex.core.util.ClassUtil;
+import com.mybatisflex.core.util.FieldWrapper;
+import org.apache.ibatis.reflection.Reflector;
+import org.apache.ibatis.reflection.TypeParameterResolver;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.*;
+
+abstract class Relation {
+
+ protected Class selfEntityClass;
+ protected Field relationField;
+ protected FieldWrapper relationFieldWrapper;
+
+ protected Field selfField;
+ protected FieldWrapper selfFieldWrapper;
+
+ protected Field targetField;
+ protected Class> targetEntityClass;
+ protected TableInfo targetTableInfo;
+ protected FieldWrapper targetFieldWrapper;
+
+ public Relation(String selfField, String targetField, Class entityClass, Field relationField) {
+ this.selfEntityClass = entityClass;
+ this.relationField = relationField;
+ this.relationFieldWrapper = FieldWrapper.of(entityClass, relationField.getName());
+
+ this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equals(selfField));
+ this.selfFieldWrapper = FieldWrapper.of(entityClass, selfField);
+
+
+ Reflector reflector = new Reflector(entityClass);
+ Class> targetClass = reflector.getGetterType(relationField.getName());
+
+ if (Collection.class.isAssignableFrom(targetClass)) {
+ Type genericType = TypeParameterResolver.resolveFieldType(relationField, entityClass);
+ if (genericType instanceof ParameterizedType) {
+ this.targetEntityClass = (Class>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
+ }
+ } else if (targetClass.isArray()) {
+ this.targetEntityClass = targetClass.getComponentType();
+ } else {
+ this.targetEntityClass = targetClass;
+ }
+
+ this.targetField = ClassUtil.getFirstField(targetEntityClass, field -> field.getName().equals(targetField));
+ this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField);
+
+ this.targetTableInfo = TableInfoFactory.ofEntityClass(targetEntityClass);
+ }
+
+
+ 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;
+ }
+
+ protected Set getSelfFieldValues(List list) {
+ if (list == null || list.isEmpty()) {
+ return Collections.emptySet();
+ }
+
+ Set values = new LinkedHashSet<>();
+ list.forEach(self -> {
+ Object value = selfFieldWrapper.get(self);
+ if (value != null && !"".equals(value)) {
+ values.add(value);
+ }
+ });
+ return values;
+ }
+
+ public abstract QueryWrapper toQueryWrapper(List selfEntities);
+
+ public abstract void map(List selfEntities, List> mappedObjectList, BaseMapper> mapper);
+
+ public Class> getMappingType() {
+ return relationFieldWrapper.getMappingType();
+ }
+}
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
new file mode 100644
index 00000000..e043e88d
--- /dev/null
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/relation/RelationManager.java
@@ -0,0 +1,92 @@
+/*
+ * 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.annotation.RelationManyToMany;
+import com.mybatisflex.annotation.RelationManyToOne;
+import com.mybatisflex.annotation.RelationOneToMany;
+import com.mybatisflex.annotation.RelationOneToOne;
+import com.mybatisflex.core.BaseMapper;
+import com.mybatisflex.core.query.QueryWrapper;
+import com.mybatisflex.core.util.ClassUtil;
+import com.mybatisflex.core.util.CollectionUtil;
+import org.apache.ibatis.util.MapUtil;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class RelationManager {
+
+ private static Map, List> classRelations = new ConcurrentHashMap<>();
+
+ private static List getRelations(Class> clazz) {
+ return MapUtil.computeIfAbsent(classRelations, clazz, RelationManager::doGetRelations);
+ }
+
+ private static List doGetRelations(Class> entityClass) {
+ List allFields = ClassUtil.getAllFields(entityClass);
+ List relations = new ArrayList<>();
+ for (Field field : allFields) {
+ RelationManyToMany manyToManyAnnotation = field.getAnnotation(RelationManyToMany.class);
+ if (manyToManyAnnotation != null) {
+ relations.add(new ManyToMany<>(manyToManyAnnotation, entityClass, field));
+ }
+
+ RelationManyToOne manyToOneAnnotation = field.getAnnotation(RelationManyToOne.class);
+ if (manyToOneAnnotation != null) {
+ relations.add(new ManyToOne<>(manyToOneAnnotation, entityClass, field));
+ }
+
+ RelationOneToMany oneToManyAnnotation = field.getAnnotation(RelationOneToMany.class);
+ if (oneToManyAnnotation != null) {
+ relations.add(new OneToMany<>(oneToManyAnnotation, entityClass, field));
+ }
+
+ RelationOneToOne oneToOneAnnotation = field.getAnnotation(RelationOneToOne.class);
+ if (oneToOneAnnotation != null) {
+ relations.add(new OneToOne<>(oneToOneAnnotation, entityClass, field));
+ }
+ }
+ return relations;
+ }
+
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static void queryRelations(BaseMapper> mapper, List entities) {
+ if (CollectionUtil.isEmpty(entities)) {
+ return;
+ }
+
+ Class objectClass = (Class) entities.get(0).getClass();
+ List relations = getRelations(objectClass);
+ if (relations.isEmpty()) {
+ return;
+ }
+
+ relations.forEach(relation -> {
+ QueryWrapper queryWrapper = relation.toQueryWrapper(entities);
+ Class> mappingType = relation.getMappingType();
+
+ List> targetObjectList = mapper.selectListByQueryAs(queryWrapper, mappingType);
+ if (CollectionUtil.isNotEmpty(targetObjectList)) {
+ relation.map(entities, targetObjectList, mapper);
+ }
+ });
+ }
+}
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Row.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Row.java
index d809d851..bdfeee6b 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Row.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Row.java
@@ -130,6 +130,21 @@ public class Row extends LinkedHashMap implements UpdateWrapper
return result != null ? result : defaultValue;
}
+ public Object getIgnoreCase(String key){
+ for (String innerKey : keySet()) {
+ if (innerKey.equalsIgnoreCase(key)){
+ return super.get(innerKey);
+ }
+ }
+ return null;
+ }
+
+
+ public Object getIgnoreCase(String key, Object defaultValue){
+ Object result = getIgnoreCase(key);
+ return result != null ? result : defaultValue;
+ }
+
@Override
public Object put(String key, Object value) {
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/FieldWrapper.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/FieldWrapper.java
index 59dfc80f..21f7c28e 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/FieldWrapper.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/FieldWrapper.java
@@ -26,6 +26,7 @@ public class FieldWrapper {
private Class> fieldType;
private Class> mappingType;
+ private Method getterMethod;
private Method setterMethod;
public static FieldWrapper of(Class> clazz, String fieldName) {
@@ -49,10 +50,11 @@ public class FieldWrapper {
throw new IllegalStateException("Can not find field \"" + fieldName + "\" in class: " + clazz);
}
+ String setterName = "set" + StringUtil.firstCharToUpperCase(fieldName);
Method setter = ClassUtil.getFirstMethod(clazz, method ->
method.getParameterCount() == 1
&& Modifier.isPublic(method.getModifiers())
- && method.getName().equals("set" + StringUtil.firstCharToUpperCase(fieldName)));
+ && method.getName().equals(setterName));
if (setter == null) {
throw new IllegalStateException("Can not find method \"set" + StringUtil.firstCharToUpperCase(fieldName) + "\" in class: " + clazz);
@@ -63,6 +65,11 @@ public class FieldWrapper {
fieldWrapper.mappingType = parseMappingType(findField);
fieldWrapper.setterMethod = setter;
+ String[] getterNames = new String[]{"get" + StringUtil.firstCharToUpperCase(fieldName), "is" + StringUtil.firstCharToUpperCase(fieldName)};
+ fieldWrapper.getterMethod = ClassUtil.getFirstMethod(clazz, method -> method.getParameterCount() == 0
+ && Modifier.isPublic(method.getModifiers())
+ && ArrayUtil.contains(getterNames, method.getName()));
+
wrapperMap.put(fieldName, fieldWrapper);
}
}
@@ -97,6 +104,14 @@ public class FieldWrapper {
}
}
+ public Object get(Object target) {
+ try {
+ return getterMethod.invoke(target);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
public Class> getFieldType() {
return fieldType;
}
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/MapperUtil.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/MapperUtil.java
index 80f9114a..2cf249ba 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/MapperUtil.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/MapperUtil.java
@@ -21,6 +21,7 @@ import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.field.FieldQuery;
import com.mybatisflex.core.field.FieldQueryBuilder;
import com.mybatisflex.core.query.*;
+import com.mybatisflex.core.relation.RelationManager;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
@@ -165,7 +166,18 @@ public class MapperUtil {
}
- private static Class> getWrapType(Class> type) {
+ public static Entity queryRelations(BaseMapper> mapper, Entity entity) {
+ queryRelations(mapper,Collections.singletonList(entity));
+ return entity;
+ }
+
+ public static List queryRelations(BaseMapper> mapper, List entities) {
+ RelationManager.queryRelations(mapper, entities);
+ return entities;
+ }
+
+
+ public static Class> getWrapType(Class> type) {
if (ClassUtil.canInstance(type.getModifiers())) {
return type;
}
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
new file mode 100644
index 00000000..9aa5fbdb
--- /dev/null
+++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Account.java
@@ -0,0 +1,109 @@
+/*
+ * 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.*;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Table(value = "tb_account")
+public class Account implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id(keyType = KeyType.Auto)
+ private Long id;
+
+ private String userName;
+
+ private int age;
+
+// @RelationOneToOne(selfField = "id", targetField = "accountId")
+ private IDCard idCard;
+
+// @RelationOneToMany(selfField = "id", targetField = "accountId")
+ private List books;
+
+ @RelationManyToMany(
+ joinTable = "tb_role_mapping",
+ selfField = "id", joinSelfColumn = "account_id",
+ targetField = "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 "Account{" +
+ "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/Book.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Book.java
new file mode 100644
index 00000000..ec2487c9
--- /dev/null
+++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Book.java
@@ -0,0 +1,88 @@
+/*
+ * 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.RelationManyToOne;
+import com.mybatisflex.annotation.Table;
+
+import java.io.Serializable;
+
+@Table(value = "tb_book")
+public class Book implements Serializable {
+
+ private Long id;
+
+ private Long accountId;
+
+ private String title;
+
+ private String content;
+
+ @RelationManyToOne(selfField = "accountId",targetField = "id")
+ private Account account;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(Long accountId) {
+ this.accountId = accountId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public Account getAccount() {
+ return account;
+ }
+
+ public void setAccount(Account account) {
+ this.account = account;
+ }
+
+ @Override
+ public String toString() {
+ return "Book{" +
+ "id=" + id +
+ ", accountId=" + accountId +
+ ", title='" + title + '\'' +
+ ", content='" + content + '\'' +
+ ", account=" + account +
+ '}';
+ }
+}
diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/IDCard.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/IDCard.java
new file mode 100644
index 00000000..9ace096a
--- /dev/null
+++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/IDCard.java
@@ -0,0 +1,64 @@
+/*
+ * 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.Table;
+
+import java.io.Serializable;
+
+@Table(value = "tb_idcard")
+public class IDCard implements Serializable {
+
+ private Long accountId;
+
+ private String cardNo;
+
+ private String content;
+
+ public Long getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(Long accountId) {
+ this.accountId = accountId;
+ }
+
+ public String getCardNo() {
+ return cardNo;
+ }
+
+ public void setCardNo(String cardNo) {
+ this.cardNo = cardNo;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ @Override
+ public String toString() {
+ return "IDCard{" +
+ "accountId=" + accountId +
+ ", cardNo='" + cardNo + '\'' +
+ ", content='" + content + '\'' +
+ '}';
+ }
+}
diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/RelationsTester.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/RelationsTester.java
new file mode 100644
index 00000000..4ad946ac
--- /dev/null
+++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/RelationsTester.java
@@ -0,0 +1,91 @@
+/**
+ * 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.core.MybatisFlexBootstrap;
+import com.mybatisflex.core.audit.AuditManager;
+import com.mybatisflex.core.audit.ConsoleMessageCollector;
+import com.mybatisflex.core.audit.MessageCollector;
+import com.mybatisflex.core.relation.RelationManager;
+import com.mybatisflex.test.relation.mapper.AccountMapper;
+import com.mybatisflex.test.relation.mapper.BookMapper;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
+
+import javax.sql.DataSource;
+import java.util.List;
+
+
+public class RelationsTester {
+
+ static AccountMapper accountMapper;
+ static BookMapper bookMapper;
+
+ @BeforeClass
+ public static void init() {
+ DataSource dataSource = new EmbeddedDatabaseBuilder()
+ .setType(EmbeddedDatabaseType.H2)
+ .addScript("relation/onetoone/schema.sql")
+ .addScript("relation/onetoone/data.sql")
+ .build();
+
+ MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()
+ .setDataSource(dataSource)
+ .addMapper(AccountMapper.class)
+ .addMapper(BookMapper.class)
+ .start();
+
+ //开启审计功能
+ AuditManager.setAuditEnable(true);
+
+ //设置 SQL 审计收集器
+ MessageCollector collector = new ConsoleMessageCollector();
+ AuditManager.setMessageCollector(collector);
+
+ accountMapper = bootstrap.getMapper(AccountMapper.class);
+ bookMapper = bootstrap.getMapper(BookMapper.class);
+ }
+
+
+ @Test
+ public void testOneToOne() {
+ List accounts = accountMapper.selectAll();
+ System.out.println(">>>>>>1: " + accounts);
+ RelationManager.queryRelations(accountMapper, accounts);
+ System.out.println(">>>>>>2: " + accounts);
+ }
+
+
+ @Test
+ public void testManyToOne() {
+ List books = bookMapper.selectAll();
+ System.out.println(">>>>>>1: " + books);
+ RelationManager.queryRelations(bookMapper, books);
+ System.out.println(">>>>>>2: " + books);
+ }
+
+ @Test
+ public void testManyToMany1() {
+ List accounts = accountMapper.selectAll();
+ System.out.println(">>>>>>1: " + accounts);
+ RelationManager.queryRelations(accountMapper, accounts);
+ System.out.println(">>>>>>2: " + accounts);
+ }
+
+
+}
diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Role.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Role.java
new file mode 100644
index 00000000..f94fe99c
--- /dev/null
+++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/relation/onetoone/Role.java
@@ -0,0 +1,71 @@
+/*
+ * 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 com.mybatisflex.annotation.Table;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Table(value = "tb_role")
+public class Role implements Serializable {
+
+ private Long id;
+ private String name;
+
+
+ @RelationManyToMany(
+ joinTable = "tb_role_mapping",
+ selfField = "id", joinSelfColumn = "role_id",
+ targetField = "id", joinTargetColumn = "account_id"
+ )
+ private List accounts;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getAccounts() {
+ return accounts;
+ }
+
+ public void setAccounts(List accounts) {
+ this.accounts = accounts;
+ }
+
+ @Override
+ public String toString() {
+ return "Role{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", accounts=" + accounts +
+ '}';
+ }
+}
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
new file mode 100644
index 00000000..498b46dc
--- /dev/null
+++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/data.sql
@@ -0,0 +1,30 @@
+INSERT INTO tb_account
+VALUES (1, '张三', 18),
+ (2, '王麻子叔叔', 19);
+
+
+INSERT INTO tb_idcard
+VALUES (1,'0001', '内容1'),
+ (2,'0002', '内容2');
+
+INSERT INTO tb_book
+VALUES (1,1,'图书1', '内容1'),
+ (2,2,'图书2', '内容2'),
+ (3,1,'图书3', '内容2'),
+ (4,1,'图书4', '内容2'),
+ (5,1,'图书5', '内容2'),
+ (6,2,'图书6', '内容2');
+
+
+
+INSERT INTO tb_role
+VALUES (1,'角色1'),
+ (2,'角色2'),
+ (3,'角色3');
+
+INSERT INTO tb_role_mapping
+VALUES (1,1),
+ (1,3),
+ (2,1),
+ (2,2),
+ (2,3);
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
new file mode 100644
index 00000000..5a72e81c
--- /dev/null
+++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/resources/relation/onetoone/schema.sql
@@ -0,0 +1,35 @@
+CREATE TABLE IF NOT EXISTS `tb_account`
+(
+ `id` INTEGER auto_increment,
+ `user_name` VARCHAR(100),
+ `age` Integer
+);
+
+
+CREATE TABLE IF NOT EXISTS `tb_idcard`
+(
+ `account_id` Integer,
+ `card_no` VARCHAR(100),
+ `content` text
+);
+
+
+CREATE TABLE IF NOT EXISTS `tb_book`
+(
+ `id` INTEGER auto_increment,
+ `account_id` Integer,
+ `title` VARCHAR(100),
+ `content` text
+);
+
+CREATE TABLE IF NOT EXISTS `tb_role`
+(
+ `id` INTEGER auto_increment,
+ `name` VARCHAR(100)
+);
+
+CREATE TABLE IF NOT EXISTS `tb_role_mapping`
+(
+ `account_id` INTEGER ,
+ `role_id` INTEGER
+);
\ No newline at end of file