diff --git a/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java b/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java index cd1fa0b..54df8c8 100644 --- a/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java +++ b/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java @@ -10,10 +10,13 @@ import com.github.yulichang.toolkit.Constant; import com.github.yulichang.toolkit.ReflectionKit; import com.github.yulichang.toolkit.support.SelectColumn; import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.github.yulichang.wrapper.resultmap.MybatisLabel; +import com.github.yulichang.wrapper.resultmap.Result; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ResultFlag; import org.apache.ibatis.mapping.ResultMap; import org.apache.ibatis.mapping.ResultMapping; import org.apache.ibatis.plugin.Interceptor; @@ -30,6 +33,7 @@ import org.apache.ibatis.type.UnknownTypeHandler; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * 连表拦截器 @@ -53,11 +57,6 @@ public class MPJInterceptor implements Interceptor { */ private static final Map> MS_CACHE = new ConcurrentHashMap<>(); - /** - * 打印 MPJ resultMap - */ - private static final boolean printResultMap = false; - @Override @SuppressWarnings({"Java8MapApi", "unchecked"}) @@ -137,10 +136,7 @@ public class MPJInterceptor implements Interceptor { if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) { builder.keyProperty(String.join(StringPool.COMMA, ms.getKeyProperties())); } - List resultMaps = new ArrayList<>(); - ResultMap resultMap = buildResultMap(ms, resultType, ew); - resultMaps.add(resultMap); - printResultMap(resultMap); + List resultMaps = buildResultMap(ms, resultType, ew); builder.resultMaps(resultMaps); return builder.build(); } @@ -149,65 +145,94 @@ public class MPJInterceptor implements Interceptor { * 构建resultMap TODO 可以用lambda简化代码 */ @SuppressWarnings({"rawtypes", "unchecked"}) - private ResultMap buildResultMap(MappedStatement ms, Class resultType, Object obj) { + private List buildResultMap(MappedStatement ms, Class resultType, Object obj) { + List result = new ArrayList<>(); TableInfo tableInfo = TableInfoHelper.getTableInfo(resultType); - String currentNamespace = ms.getResource().split(StringPool.SPACE)[0]; - String id = currentNamespace + StringPool.DOT + Constants.MYBATIS_PLUS + StringPool.UNDERSCORE + resultType.getSimpleName(); - + String id = ms.getId() + StringPool.DOT + Constants.MYBATIS_PLUS + StringPool.UNDERSCORE + resultType.getName(); if (!(obj instanceof MPJLambdaWrapper) || Map.class.isAssignableFrom(resultType) || ReflectionKit.isPrimitiveOrWrapper(resultType) || Collection.class.isAssignableFrom(resultType)) { - return getDefaultResultMap(tableInfo, ms, resultType, id); + result.add(getDefaultResultMap(tableInfo, ms, resultType, id)); + return result; } MPJLambdaWrapper wrapper = (MPJLambdaWrapper) obj; Map fieldMap = ReflectionKit.getFieldMap(resultType); - - if (wrapper.isResultMap()) { - //TODO - return new ResultMap.Builder(ms.getConfiguration(), id, resultType, EMPTY_RESULT_MAPPING).build(); - } else { - List columnList = wrapper.getSelectColumns(); - List resultMappings = new ArrayList<>(columnList.size()); - columnList.forEach(i -> { - TableFieldInfo info = i.getTableFieldInfo(); - if (StringUtils.isNotBlank(i.getAlias())) { - //优先别名查询 selectFunc selectAs - ResultMapping.Builder builder = new ResultMapping.Builder(ms.getConfiguration(), i.getAlias(), - i.getAlias(), getAliasField(resultType, fieldMap, i.getAlias())); - if (i.getFuncEnum() == null || StringUtils.isBlank(i.getFuncEnum().getSql())) { - if (info != null && info.getTypeHandler() != null && info.getTypeHandler() != UnknownTypeHandler.class) { - Field f = fieldMap.get(i.getAlias()); - if (f.getType() == info.getField().getType()) { - builder.typeHandler(getTypeHandler(ms, info)); - } + List columnList = wrapper.getSelectColumns(); + List resultMappings = new ArrayList<>(columnList.size()); + columnList.forEach(i -> { + TableFieldInfo info = i.getTableFieldInfo(); + if (StringUtils.isNotBlank(i.getAlias())) { + //优先别名查询 selectFunc selectAs + ResultMapping.Builder builder = new ResultMapping.Builder(ms.getConfiguration(), i.getAlias(), + i.getAlias(), getAliasField(resultType, fieldMap, i.getAlias())); + if (i.getFuncEnum() == null || StringUtils.isBlank(i.getFuncEnum().getSql())) { + if (info != null && info.getTypeHandler() != null && info.getTypeHandler() != UnknownTypeHandler.class) { + Field f = fieldMap.get(i.getAlias()); + if (f.getType() == info.getField().getType()) { + builder.typeHandler(getTypeHandler(ms, info)); + } + } + } + resultMappings.add(builder.build()); + } else if (info != null) { + // select selectAll selectAsClass + if (i.getFuncEnum() == null || StringUtils.isBlank(i.getFuncEnum().getSql())) { + ResultMapping.Builder builder = new ResultMapping.Builder(ms.getConfiguration(), info.getProperty(), + info.getColumn(), info.getPropertyType()); + if (info.getTypeHandler() != null && info.getTypeHandler() != UnknownTypeHandler.class) { + Field f = fieldMap.get(info.getProperty()); + if (f != null && f.getType() == info.getField().getType()) { + builder.typeHandler(getTypeHandler(ms, info)); } } resultMappings.add(builder.build()); - } else if (info != null) { - // select selectAll selectAsClass - if (i.getFuncEnum() == null || StringUtils.isBlank(i.getFuncEnum().getSql())) { - ResultMapping.Builder builder = new ResultMapping.Builder(ms.getConfiguration(), info.getProperty(), - info.getColumn(), info.getPropertyType()); - if (info.getTypeHandler() != null && info.getTypeHandler() != UnknownTypeHandler.class) { - Field f = fieldMap.get(info.getProperty()); - if (f != null && f.getType() == info.getField().getType()) { - builder.typeHandler(getTypeHandler(ms, info)); - } - } - resultMappings.add(builder.build()); - } else { - resultMappings.add(new ResultMapping.Builder(ms.getConfiguration(), info.getProperty(), info.getColumn(), info.getPropertyType()).build()); - } } else { - // 主键列 - resultMappings.add(new ResultMapping.Builder(ms.getConfiguration(), i.getTagProperty(), i.getColumnName(), - getAliasField(resultType, fieldMap, i.getTagProperty())).build()); + resultMappings.add(new ResultMapping.Builder(ms.getConfiguration(), info.getProperty(), info.getColumn(), info.getPropertyType()).build()); } - }); - //移除result中不存在的标签 - resultMappings.removeIf(i -> !fieldMap.containsKey(i.getProperty())); - return new ResultMap.Builder(ms.getConfiguration(), id, resultType, resultMappings).build(); + } else { + // 主键列 + resultMappings.add(new ResultMapping.Builder(ms.getConfiguration(), i.getTagProperty(), i.getColumnName(), + i.getKeyType()).build()); + } + }); + //移除result中不存在的标签 + Set columnSet = resultMappings.stream().map(ResultMapping::getColumn).collect(Collectors.toSet()); + resultMappings.removeIf(i -> !fieldMap.containsKey(i.getProperty())); + if (wrapper.isResultMap()) { + //先不考虑 去重 看一下能否实现 + List> mybatisLabel = wrapper.getResultMapMybatisLabel(); + for (MybatisLabel mpjColl : mybatisLabel) { + List list = mpjColl.getResultList(); + if (CollectionUtils.isEmpty(list)) { + continue; + } + List childMapping = new ArrayList<>(list.size()); + for (Result r : list) { + String columnName = r.getColumn(); + columnName = getColumn(columnSet, columnName); + columnList.add(SelectColumn.of(mpjColl.getEntityClass(), r.getColumn(), null, + Objects.equals(columnName, r.getColumn()) ? null : columnName, null, null, null)); + ResultMapping.Builder builder = new ResultMapping.Builder(ms.getConfiguration(), r.getProperty(), columnName, r.getJavaType()); + if (r.isId()) { + builder.flags(Collections.singletonList(ResultFlag.ID)); + } + childMapping.add(builder.build()); + } + String childId = id + StringPool.UNDERSCORE + mpjColl.getEntityClass().getName() + StringPool.UNDERSCORE + + mpjColl.getOfType().getName() + StringPool.UNDERSCORE + mpjColl.getLabelType() + StringPool.UNDERSCORE + + childMapping.stream().map(i -> (CollectionUtils.isEmpty(i.getFlags()) ? ResultFlag.CONSTRUCTOR : + i.getFlags().get(0)) + i.getProperty() + i.getColumn()).collect(Collectors.joining(StringPool.DASH)); + resultMappings.add(new ResultMapping.Builder(ms.getConfiguration(), mpjColl.getProperty()) + .javaType(mpjColl.getJavaType()) + .nestedResultMapId(childId) + .build()); + if (!ms.getConfiguration().getResultMapNames().contains(childId)) { + ms.getConfiguration().addResultMap(new ResultMap.Builder(ms.getConfiguration(), childId, mpjColl.getOfType(), childMapping).build()); + } + } } + result.add(0, new ResultMap.Builder(ms.getConfiguration(), id, resultType, resultMappings).build()); + return result; } /** @@ -222,6 +247,21 @@ public class MPJInterceptor implements Interceptor { return typeHandler; } + /** + * 列名去重 重复的添加 mpj 前缀 再重复走递归 + * + * @param pool 查询列 集合 + * @param columnName 列明 + * @return 唯一列名 + */ + private String getColumn(Set pool, String columnName) { + if (!pool.contains(columnName)) { + pool.add(columnName); + return columnName; + } + columnName = "mpj_" + StringUtils.getTargetColumn(columnName); + return getColumn(pool, columnName); + } /** * 获取非lambda的resultMap @@ -255,19 +295,4 @@ public class MPJInterceptor implements Interceptor { Assert.notNull(field, "Result Class <%s> not find Field <%s>", resultType.getSimpleName(), alias); return field.getType(); } - - /** - * 打印resultMap - * 先打开 mybatis plus 日志, 在设置 printResultMap = true 才会打印 - */ - private void printResultMap(ResultMap resultMap) { - if (resultMap == null || !printResultMap) - return; - logger.debug("===================== MPJ resultMap ========================="); - List mappings = resultMap.getResultMappings(); - logger.debug(String.format(" ", resultMap.getId(), resultMap.getType().getName())); - mappings.forEach(i -> logger.debug(String.format(" ", i.getProperty(), i.getColumn(), i.getJavaType(), i.getTypeHandler()))); - logger.debug(" "); - logger.debug("===================== end ========================="); - } } diff --git a/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java b/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java index fe30543..e782640 100644 --- a/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java +++ b/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java @@ -92,13 +92,13 @@ public final class LambdaUtils { if (info.havePK()) { map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size() + 1); map.put(formatKey(info.getKeyProperty()), new ColumnCache(info.getKeyColumn(), info.getKeySqlSelect(), - null, info.getKeyProperty(), true)); + null, info.getKeyProperty(), true, info.getKeyType())); } else { map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size()); } info.getFieldList().forEach(i -> - map.put(formatKey(i.getProperty()), new ColumnCache(i.getColumn(), i.getSqlSelect(), i, null, false)) + map.put(formatKey(i.getProperty()), new ColumnCache(i.getColumn(), i.getSqlSelect(), i, null, false, null)) ); return map; } diff --git a/src/main/java/com/github/yulichang/toolkit/support/ColumnCache.java b/src/main/java/com/github/yulichang/toolkit/support/ColumnCache.java index 54b24fa..2d4df23 100644 --- a/src/main/java/com/github/yulichang/toolkit/support/ColumnCache.java +++ b/src/main/java/com/github/yulichang/toolkit/support/ColumnCache.java @@ -21,6 +21,9 @@ public class ColumnCache extends com.baomidou.mybatisplus.core.toolkit.support.C @Getter private boolean isPK; + @Getter + private Class keyType; + @Deprecated public ColumnCache(String column, String columnSelect) { @@ -32,10 +35,11 @@ public class ColumnCache extends com.baomidou.mybatisplus.core.toolkit.support.C super(column, columnSelect, mapping); } - public ColumnCache(String column, String columnSelect, TableFieldInfo tableFieldInfo, String keyProperty, boolean isPK) { + public ColumnCache(String column, String columnSelect, TableFieldInfo tableFieldInfo, String keyProperty, boolean isPK, Class keyType) { super(column, columnSelect); this.tableFieldInfo = tableFieldInfo; this.keyProperty = keyProperty; this.isPK = isPK; + this.keyType = keyType; } } diff --git a/src/main/java/com/github/yulichang/toolkit/support/SelectColumn.java b/src/main/java/com/github/yulichang/toolkit/support/SelectColumn.java index 74498d4..cd28468 100644 --- a/src/main/java/com/github/yulichang/toolkit/support/SelectColumn.java +++ b/src/main/java/com/github/yulichang/toolkit/support/SelectColumn.java @@ -43,25 +43,31 @@ public class SelectColumn implements UniqueObject { */ private final String tagProperty; + /** + * 主键类型 + */ + private final Class keyType; + /** * 字段函数 */ private final BaseFuncEnum funcEnum; - private SelectColumn(Class clazz, String columnName, TableFieldInfo tableFieldInfo, String alias, String tagProperty, BaseFuncEnum funcEnum) { + private SelectColumn(Class clazz, String columnName, TableFieldInfo tableFieldInfo, String alias, String tagProperty, Class keyType, BaseFuncEnum funcEnum) { this.clazz = clazz; this.columnName = columnName; this.tableFieldInfo = tableFieldInfo; this.alias = alias; this.tagProperty = tagProperty; + this.keyType = keyType; this.funcEnum = funcEnum; } - public static SelectColumn of(Class clazz, String columnName, TableFieldInfo tableFieldInfo, String alias, String tagProperty, BaseFuncEnum funcEnum) { + public static SelectColumn of(Class clazz, String columnName, TableFieldInfo tableFieldInfo, String alias, String tagProperty, Class keyType, BaseFuncEnum funcEnum) { if (tagProperty != null) tagProperty = StringUtils.getTargetColumn(tagProperty); - return new SelectColumn(clazz, columnName, tableFieldInfo, alias, tagProperty, funcEnum); + return new SelectColumn(clazz, columnName, tableFieldInfo, alias, tagProperty, keyType, funcEnum); } /** diff --git a/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java b/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java index 8cb7d6e..b1c351c 100644 --- a/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java +++ b/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java @@ -18,12 +18,14 @@ import com.github.yulichang.wrapper.enums.BaseFuncEnum; import com.github.yulichang.wrapper.interfaces.LambdaJoin; import com.github.yulichang.wrapper.interfaces.Query; import com.github.yulichang.wrapper.interfaces.on.OnFunction; -import com.github.yulichang.wrapper.resultmap.Collection; +import com.github.yulichang.wrapper.resultmap.LabelType; import com.github.yulichang.wrapper.resultmap.MFunc; +import com.github.yulichang.wrapper.resultmap.MybatisLabel; import lombok.Getter; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -61,10 +63,10 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper> onWrappers = new ArrayList<>(); /** - * 敌对多映射关系 + * 映射关系 */ @Getter - private final List> resultMapCollection = new ArrayList<>(); + private final List> resultMapMybatisLabel = new ArrayList<>(); /** * 是否构建是否存在一对多 */ @@ -140,13 +142,35 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper s : columns) { ColumnCache cache = getCache(s); selectColumns.add(SelectColumn.of(LambdaUtils.getEntityClass(s), cache.getColumn(), cache.getTableFieldInfo(), - null, cache.getTableFieldInfo() == null ? cache.getKeyProperty() : cache.getTableFieldInfo().getProperty(), null)); + null, cache.getTableFieldInfo() == null ? cache.getKeyProperty() : cache.getTableFieldInfo().getProperty(), cache.getKeyType(), null)); } } return typedThis; } - // TODO 重载重复代码简化 + /** + * 一对多查询 调用此方法发必需要调用对应的 left join / right join ... 连表方法,否则会报错 + *

+ * 举例 UserDO UserAddressDO 为一对多关系 UserDTO 为结果类 + *

+     *     MPJLambdaQueryWrapper wrapper = new MPJLambdaQueryWrapper();
+     *     wrapper.selectAll(UserDO.class)
+     *            .selectCollection(UserAddressDO.class, UserDTO::getAddressListDTO)
+     *            .leftJoin(UserAddressDO.class, ...... )
+     *            .eq(...)
+     *            ...
+     * 
+     * 会自动将 UserAddressDO类中相同属性的字段 以mybatis的方式映射到UserDTO.addressListDTO属性中
+     *
+     * @since 1.2.5
+     *
+     * @param child    连表数据库实体类
+     * @param dtoField 包装类对应的属性
+     * @param       包装类
+     * @param       对多数据库实体类
+     * @param       包装类集合泛型
+     * @param       包装类集合字段泛型
+     */
     public > MPJLambdaWrapper selectCollection(Class child, SFunction dtoField) {
         String dtoFieldName = LambdaUtils.getName(dtoField);
         Class dtoClass = LambdaUtils.getEntityClass(dtoField);
@@ -154,23 +178,51 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper genericType = ReflectionKit.getGenericType(field);
-        Collection.Builder builder;
+        MybatisLabel.Builder builder;
         if (genericType == null || genericType.isAssignableFrom(child)) {
             //找不到集合泛型 List List List , 直接查询数据库实体
-            builder = new Collection.Builder<>(dtoFieldName, child, field.getType());
+            builder = new MybatisLabel.Builder<>(LabelType.COLLECTION, dtoFieldName, child, field.getType());
         } else {
             Class ofType = (Class) genericType;
             if (ReflectionKit.isPrimitiveOrWrapper(ofType)) {
                 throw new MPJException("collection 不支持基本数据类型");
             }
-            builder = new Collection.Builder<>(dtoFieldName, child, field.getType(), ofType, true);
+            builder = new MybatisLabel.Builder<>(LabelType.COLLECTION, dtoFieldName, child, field.getType(), ofType, true);
         }
-        this.resultMapCollection.add(builder.build());
+        this.resultMapMybatisLabel.add(builder.build());
         return typedThis;
     }
 
+    /**
+     * 一对多查询 调用此方法发必需要调用对应的 left join / right join ... 连表方法,否则会报错
+     * 

+ * 举例 UserDO UserAddressDO 为一对多关系 UserDTO 为结果类 + *

+     *     MPJLambdaQueryWrapper wrapper = new MPJLambdaQueryWrapper();
+     *     wrapper.selectAll(UserDO.class)
+     *            .selectCollection(UserAddressDO.class, UserDTO::getAddressListDTO, map -> map
+     *                 .id(UserAddressDO::getId, AddressDTO::getId)                     //如果属性名一致 可以传一个
+     *                 .result(UserAddressDO::getUserId)                                //如果属性名一致 可以传一个
+     *                 .result(UserAddressDO::getAddress, AddressDTO::getAddress)))     //如果属性名一致 可以传一个
+     *            .leftJoin(UserAddressDO.class, ...... )
+     *            .eq(...)
+     *            ...
+     * 
+     *
+     * 会自动将 UserAddressDO类中指定的字段 以mybatis的方式映射到UserDTO.addressListDTO属性中
+     *
+     * @since 1.2.5
+     *
+     * @param child      连表数据库实体类
+     * @param dtoField   包装类对应的属性
+     * @param collection collection标签内容
+     * @param         包装类
+     * @param         对多数据库实体类
+     * @param         包装类集合泛型
+     * @param         包装类集合字段泛型
+     */
     public > MPJLambdaWrapper
-    selectCollection(Class child, SFunction dtoField, MFunc> collection) {
+    selectCollection(Class child, SFunction dtoField, MFunc> collection) {
         String dtoFieldName = LambdaUtils.getName(dtoField);
         Class dtoClass = LambdaUtils.getEntityClass(dtoField);
         Field field = ReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
@@ -178,13 +230,49 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper genericType = ReflectionKit.getGenericType(field);
         Class ofType = (Class) genericType;
-        Collection.Builder builder = new Collection.Builder<>(dtoFieldName, child, field.getType(), ofType, false);
-        this.resultMapCollection.add(collection.apply(builder).build());
+        MybatisLabel.Builder builder = new MybatisLabel.Builder<>(LabelType.COLLECTION, dtoFieldName, child, field.getType(), ofType, false);
+        this.resultMapMybatisLabel.add(collection.apply(builder).build());
         return typedThis;
     }
 
-    public  MPJLambdaWrapper selectAssociation(Class child, SFunction dtoField, MFunc> collection) {
-        //TODO
+    /**
+     * 对一查询 用法参考 selectCollection
+     *
+     * @since 1.2.5
+     */
+    public  MPJLambdaWrapper selectAssociation(Class child, SFunction dtoField) {
+        String dtoFieldName = LambdaUtils.getName(dtoField);
+        Class dtoClass = LambdaUtils.getEntityClass(dtoField);
+        Map fieldMap = ReflectionKit.getFieldMap(dtoClass);
+        Field field = fieldMap.get(dtoFieldName);
+        Assert.isFalse(Collection.class.isAssignableFrom(field.getType()), "association 不支持集合类");
+        if (ReflectionKit.isPrimitiveOrWrapper(field.getType())) {
+            throw new MPJException("association 不支持基本数据类型");
+        }
+        this.resultMap = true;
+        MybatisLabel.Builder builder;
+        builder = new MybatisLabel.Builder<>(LabelType.ASSOCIATION, dtoFieldName, child, field.getType(), (Class) field.getType(), true);
+        this.resultMapMybatisLabel.add(builder.build());
+        return typedThis;
+    }
+
+    /**
+     * 对一查询 用法参考 selectCollection
+     *
+     * @since 1.2.5
+     */
+    public  MPJLambdaWrapper selectAssociation(Class child, SFunction dtoField,
+                                                           MFunc> collection) {
+        String dtoFieldName = LambdaUtils.getName(dtoField);
+        Class dtoClass = LambdaUtils.getEntityClass(dtoField);
+        Field field = ReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
+        this.resultMap = true;
+        Assert.isFalse(Collection.class.isAssignableFrom(field.getType()), "association 不支持集合类");
+        if (ReflectionKit.isPrimitiveOrWrapper(field.getType())) {
+            throw new MPJException("association 不支持基本数据类型");
+        }
+        MybatisLabel.Builder builder = new MybatisLabel.Builder<>(LabelType.ASSOCIATION, dtoFieldName, child, field.getType(), (Class) child, false);
+        this.resultMapMybatisLabel.add(collection.apply(builder).build());
         return typedThis;
     }
 
@@ -194,7 +282,7 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper", entityClass.getName());
         info.getFieldList().stream().filter(predicate).collect(Collectors.toList()).forEach(
-                i -> selectColumns.add(SelectColumn.of(entityClass, i.getColumn(), i, null, i.getProperty(), null)));
+                i -> selectColumns.add(SelectColumn.of(entityClass, i.getColumn(), i, null, i.getProperty(), null, null)));
         return typedThis;
     }
 
@@ -205,11 +293,12 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper tagFields = ReflectionKit.getFieldList(tag);
         tableInfo.getFieldList().forEach(i -> {
             if (tagFields.stream().anyMatch(f -> f.getName().equals(i.getProperty()))) {
-                selectColumns.add(SelectColumn.of(source, i.getColumn(), i, null, i.getProperty(), null));
+                selectColumns.add(SelectColumn.of(source, i.getColumn(), i, null, i.getProperty(), null, null));
             }
         });
         if (tableInfo.havePK() && tagFields.stream().anyMatch(i -> i.getName().equals(tableInfo.getKeyProperty()))) {
-            selectColumns.add(SelectColumn.of(source, tableInfo.getKeyColumn(), null, null, tableInfo.getKeyProperty(), null));
+            selectColumns.add(SelectColumn.of(source, tableInfo.getKeyColumn(), null, null,
+                    tableInfo.getKeyProperty(), tableInfo.getKeyType(), null));
         }
         return typedThis;
     }
@@ -217,14 +306,16 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper MPJLambdaWrapper selectAs(SFunction column, String alias) {
         ColumnCache cache = getCache(column);
-        selectColumns.add(SelectColumn.of(LambdaUtils.getEntityClass(column), cache.getColumn(), cache.getTableFieldInfo(), alias, null, null));
+        selectColumns.add(SelectColumn.of(LambdaUtils.getEntityClass(column), cache.getColumn(), cache.getTableFieldInfo(),
+                alias, null, cache.getKeyType(), null));
         return typedThis;
     }
 
     public  MPJLambdaWrapper selectFunc(boolean condition, BaseFuncEnum funcEnum, SFunction column, String alias) {
         if (condition) {
             ColumnCache cache = getCache(column);
-            selectColumns.add(SelectColumn.of(LambdaUtils.getEntityClass(column), cache.getColumn(), cache.getTableFieldInfo(), alias, alias, funcEnum));
+            selectColumns.add(SelectColumn.of(LambdaUtils.getEntityClass(column), cache.getColumn(),
+                    cache.getTableFieldInfo(), alias, alias, cache.getKeyType(), funcEnum));
         }
         return typedThis;
     }
@@ -232,7 +323,7 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper selectFunc(boolean condition, BaseFuncEnum funcEnum, Object column, String alias) {
         if (condition) {
-            selectColumns.add(SelectColumn.of(null, column.toString(), null, alias, alias, funcEnum));
+            selectColumns.add(SelectColumn.of(null, column.toString(), null, alias, alias, null, funcEnum));
         }
         return typedThis;
     }
@@ -241,10 +332,11 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper %s", clazz);
         if (info.havePK()) {
-            selectColumns.add(SelectColumn.of(clazz, info.getKeyColumn(), null, null, info.getKeyProperty(), null));
+            selectColumns.add(SelectColumn.of(clazz, info.getKeyColumn(), null, null,
+                    info.getKeyProperty(), info.getKeyType(), null));
         }
         info.getFieldList().forEach(c ->
-                selectColumns.add(SelectColumn.of(clazz, c.getColumn(), c, null, c.getProperty(), null)));
+                selectColumns.add(SelectColumn.of(clazz, c.getColumn(), c, null, c.getProperty(), null, null)));
         return typedThis;
     }
 
diff --git a/src/main/java/com/github/yulichang/wrapper/resultmap/LabelType.java b/src/main/java/com/github/yulichang/wrapper/resultmap/LabelType.java
new file mode 100644
index 0000000..6d33b2a
--- /dev/null
+++ b/src/main/java/com/github/yulichang/wrapper/resultmap/LabelType.java
@@ -0,0 +1,5 @@
+package com.github.yulichang.wrapper.resultmap;
+
+public enum LabelType {
+    COLLECTION, ASSOCIATION
+}
diff --git a/src/main/java/com/github/yulichang/wrapper/resultmap/Collection.java b/src/main/java/com/github/yulichang/wrapper/resultmap/MybatisLabel.java
similarity index 50%
rename from src/main/java/com/github/yulichang/wrapper/resultmap/Collection.java
rename to src/main/java/com/github/yulichang/wrapper/resultmap/MybatisLabel.java
index 7017ce2..f8de60e 100644
--- a/src/main/java/com/github/yulichang/wrapper/resultmap/Collection.java
+++ b/src/main/java/com/github/yulichang/wrapper/resultmap/MybatisLabel.java
@@ -3,6 +3,7 @@ package com.github.yulichang.wrapper.resultmap;
 import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
 import com.baomidou.mybatisplus.core.metadata.TableInfo;
 import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
 import com.github.yulichang.toolkit.ReflectionKit;
 import lombok.Getter;
 
@@ -20,7 +21,9 @@ import java.util.stream.Collectors;
  * @since 1.2.5
  */
 @Getter
-public class Collection {
+public class MybatisLabel {
+
+    private LabelType labelType;
 
     private String property;
 
@@ -33,26 +36,28 @@ public class Collection {
     private List resultList;
 
     //collection嵌套
-//    private List collectionList;
+    //    private List collectionList;
 
-    private Collection() {
+    private MybatisLabel() {
     }
 
+    @SuppressWarnings("unused")
     public static class Builder {
 
-        private final Collection collection;
+        private final MybatisLabel mybatisLabel;
 
         /**
          * 自动构建
          */
         @SuppressWarnings("unchecked")
-        public Builder(String property, Class entityClass, Class javaType) {
-            this.collection = new Collection<>();
-            collection.property = property;
-            collection.entityClass = entityClass;
-            collection.javaType = javaType;
-            collection.ofType = (Class) entityClass;
-            collection.resultList = new ArrayList<>();
+        public Builder(LabelType labelType, String property, Class entityClass, Class javaType) {
+            this.mybatisLabel = new MybatisLabel<>();
+            mybatisLabel.labelType = labelType;
+            mybatisLabel.property = property;
+            mybatisLabel.entityClass = entityClass;
+            mybatisLabel.javaType = javaType;
+            mybatisLabel.ofType = (Class) entityClass;
+            mybatisLabel.resultList = new ArrayList<>();
             autoBuild(true, entityClass, (Class) entityClass);
         }
 
@@ -65,30 +70,47 @@ public class Collection {
          * @param ofType      映射类
          * @param auto        自动映射数据库实体对应的字段
          */
-        public Builder(String property, Class entityClass, Class javaType, Class ofType, boolean auto) {
-            this.collection = new Collection<>();
-            collection.property = property;
-            collection.entityClass = entityClass;
-            collection.javaType = javaType;
-            collection.ofType = ofType;
-            collection.resultList = new ArrayList<>();
+        public Builder(LabelType labelType, String property, Class entityClass, Class javaType, Class ofType, boolean auto) {
+            this.mybatisLabel = new MybatisLabel<>();
+            mybatisLabel.labelType = labelType;
+            mybatisLabel.property = property;
+            mybatisLabel.entityClass = entityClass;
+            mybatisLabel.javaType = javaType;
+            mybatisLabel.ofType = ofType;
+            mybatisLabel.resultList = new ArrayList<>();
             autoBuild(auto, entityClass, ofType);
         }
 
-        public Builder id(MFunc> result) {
-            Result r = result.apply(new Result.Builder<>(true)).build();
-            collection.resultList.add(r);
+        public Builder id(SFunction entity, SFunction tag) {
+            Result.Builder builder = new Result.Builder<>(true);
+            builder.column(entity).property(tag);
+            mybatisLabel.resultList.add(builder.build());
             return this;
         }
 
-        public Builder result(MFunc> result) {
-            Result r = result.apply(new Result.Builder<>(false)).build();
-            collection.resultList.add(r);
+        public Builder id(SFunction entity) {
+            Result.Builder builder = new Result.Builder<>(true);
+            builder.column(entity);
+            mybatisLabel.resultList.add(builder.build());
             return this;
         }
 
-        public Collection build() {
-            return collection;
+        public Builder result(SFunction entity, SFunction tag) {
+            Result.Builder builder = new Result.Builder<>(false);
+            builder.column(entity).property(tag);
+            mybatisLabel.resultList.add(builder.build());
+            return this;
+        }
+
+        public Builder result(SFunction entity) {
+            Result.Builder builder = new Result.Builder<>(false);
+            builder.column(entity);
+            mybatisLabel.resultList.add(builder.build());
+            return this;
+        }
+
+        public MybatisLabel build() {
+            return mybatisLabel;
         }
 
         private void autoBuild(boolean auto, Class entityClass, Class tagClass) {
@@ -107,16 +129,15 @@ public class Collection {
                 };
                 if (entityClass == tagClass) {
                     if (tableInfo.havePK()) {
-                        collection.resultList.add(pkBuild(tableInfo));
+                        mybatisLabel.resultList.add(pkBuild(tableInfo));
                     }
-                    collection.resultList.addAll(tableInfo.getFieldList().stream().map(build).collect(Collectors.toList()));
+                    mybatisLabel.resultList.addAll(tableInfo.getFieldList().stream().map(build).collect(Collectors.toList()));
                 } else {
                     if (tableInfo.havePK() && tagMap.containsKey(tableInfo.getKeyProperty())) {
-                        collection.resultList.add(pkBuild(tableInfo));
-                    } else {
-                        collection.resultList.addAll(tableInfo.getFieldList().stream().filter(i ->
-                                tagMap.containsKey(i.getProperty())).map(build).collect(Collectors.toList()));
+                        mybatisLabel.resultList.add(pkBuild(tableInfo));
                     }
+                    mybatisLabel.resultList.addAll(tableInfo.getFieldList().stream().filter(i ->
+                            tagMap.containsKey(i.getProperty())).map(build).collect(Collectors.toList()));
                 }
             }
         }
diff --git a/src/main/java/com/github/yulichang/wrapper/resultmap/Result.java b/src/main/java/com/github/yulichang/wrapper/resultmap/Result.java
index da18258..d0249b6 100644
--- a/src/main/java/com/github/yulichang/wrapper/resultmap/Result.java
+++ b/src/main/java/com/github/yulichang/wrapper/resultmap/Result.java
@@ -4,8 +4,11 @@ import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
 import com.baomidou.mybatisplus.core.metadata.TableInfo;
 import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
 import com.baomidou.mybatisplus.core.toolkit.Assert;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
 import com.github.yulichang.toolkit.LambdaUtils;
+import com.github.yulichang.toolkit.UniqueObject;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.Setter;
@@ -20,7 +23,7 @@ import org.apache.ibatis.type.TypeHandler;
  */
 @Getter
 @Setter(AccessLevel.PACKAGE)
-public class Result {
+public class Result implements UniqueObject {
 
     private boolean isId;
 
@@ -39,6 +42,12 @@ public class Result {
     public Result() {
     }
 
+    @Override
+    public String getUniqueKey() {
+        return String.join(StringPool.DASH, String.valueOf(isId), property, column);
+    }
+
+    @SuppressWarnings({"UnusedReturnValue", "unused"})
     public static class Builder {
 
         private final Result result;
@@ -60,11 +69,21 @@ public class Result {
             Assert.notNull(tableInfo, "table not find by class <%s>", tableInfo);
             if (tableInfo.havePK() && tableInfo.getKeyProperty().equals(name)) {
                 result.column = tableInfo.getKeyColumn();
+                if (StringUtils.isBlank(result.property)) {
+                    result.property = tableInfo.getKeyProperty();
+                }
+                result.javaType = tableInfo.getKeyType();
             } else {
                 TableFieldInfo fieldInfo = tableInfo.getFieldList().stream().filter(i -> i.getField().getName().equals(name)).findFirst().orElse(null);
                 Assert.notNull(fieldInfo, "table <%s> not find column <%>", tableInfo.getTableName(), name);
                 result.column = fieldInfo.getColumn();
                 result.tableFieldInfo = fieldInfo;
+                if (StringUtils.isBlank(result.property)) {
+                    result.property = fieldInfo.getProperty();
+                }
+                result.jdbcType = fieldInfo.getJdbcType();
+                result.javaType = fieldInfo.getField().getType();
+                result.typeHandle = fieldInfo.getTypeHandler();
             }
             return this;
         }
@@ -80,7 +99,6 @@ public class Result {
         }
 
         public Result build() {
-            //TODO 检查数据完整性
             return result;
         }