mirror of
https://gitee.com/best_handsome/mybatis-plus-join
synced 2025-07-11 00:02:22 +08:00
一对一 一对多 关系映射注解实现
This commit is contained in:
parent
866bccff28
commit
a6c577ee88
@ -7,7 +7,7 @@ import com.github.yulichang.annotation.MPJMapping;
|
||||
import com.github.yulichang.annotation.MPJMappingApply;
|
||||
import com.github.yulichang.annotation.MPJMappingCondition;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -18,7 +18,7 @@ import java.util.List;
|
||||
* @author yulichang
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Data
|
||||
@Getter
|
||||
public class MPJMappingWrapper {
|
||||
|
||||
private final boolean hasFirst;
|
||||
@ -69,18 +69,18 @@ public class MPJMappingWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Apply {
|
||||
private String sql;
|
||||
private String[] val;
|
||||
private final String sql;
|
||||
private final String[] val;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Condition {
|
||||
private SqlKeyword keyword;
|
||||
private String column;
|
||||
private String[] val;
|
||||
private final SqlKeyword keyword;
|
||||
private final String column;
|
||||
private final String[] val;
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,12 @@ import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 字段属性
|
||||
@ -36,15 +39,15 @@ public class MPJTableFieldInfo {
|
||||
/**
|
||||
* 属性
|
||||
*/
|
||||
private final Field field;
|
||||
private Field field;
|
||||
/**
|
||||
* 属性
|
||||
* 数据结构是否是Map或者List<Map>
|
||||
*/
|
||||
private final boolean fieldIsMap;
|
||||
private boolean fieldIsMap;
|
||||
/**
|
||||
* 属性是否是集合
|
||||
*/
|
||||
private final boolean isCollection;
|
||||
private boolean isCollection;
|
||||
/**
|
||||
* 当前映射属性
|
||||
*/
|
||||
@ -91,22 +94,24 @@ public class MPJTableFieldInfo {
|
||||
* 关联查询条件配置
|
||||
*/
|
||||
private final MPJMappingWrapper wrapper;
|
||||
/**
|
||||
* 一对一查询结果数量不匹配是是否抛出异常
|
||||
*/
|
||||
private final boolean isThrowExp;
|
||||
|
||||
/**
|
||||
* 初始化关联字段信息
|
||||
*/
|
||||
public MPJTableFieldInfo(Class<?> entityType, MPJMapping mapping, Field field) {
|
||||
field.setAccessible(true);
|
||||
public MPJTableFieldInfo(Class<?> entityType, MPJMapping mapping, Field field1) {
|
||||
initField(field1);
|
||||
this.entityType = entityType;
|
||||
this.field = field;
|
||||
this.joinClass = mapping.tag();
|
||||
this.isCollection = Collection.class.isAssignableFrom(field.getType());
|
||||
this.isThrowExp = mapping.isThrowExp();
|
||||
this.thisMapKey = StringUtils.isBlank(mapping.thisMapKey()) ? null : mapping.thisMapKey();
|
||||
this.joinMapKey = StringUtils.isBlank(mapping.joinMapKsy()) ? null : mapping.joinMapKsy();
|
||||
this.fieldIsMap = mapping.isMap();//TODO 应该可以自动检测
|
||||
this.wrapper = new MPJMappingWrapper(mapping);
|
||||
if (this.isCollection && field.getType() != List.class && field.getType() != ArrayList.class) {
|
||||
throw new MPJException("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + field.getType().getTypeName());
|
||||
if (this.isCollection && this.field.getType() != List.class && this.field.getType() != ArrayList.class) {
|
||||
throw new MPJException("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + this.field.getType().getTypeName());
|
||||
}
|
||||
if (StringUtils.isNotBlank(mapping.joinField())) {
|
||||
this.joinProperty = mapping.joinField();
|
||||
@ -124,6 +129,28 @@ public class MPJTableFieldInfo {
|
||||
}
|
||||
}
|
||||
|
||||
private void initField(Field field) {
|
||||
field.setAccessible(true);
|
||||
this.field = field;
|
||||
this.isCollection = Collection.class.isAssignableFrom(field.getType());
|
||||
|
||||
if (Map.class.isAssignableFrom(field.getType())) {
|
||||
this.fieldIsMap = true;
|
||||
} else {
|
||||
if (field.getGenericType() instanceof ParameterizedType) {
|
||||
ParameterizedType t = (ParameterizedType) field.getGenericType();
|
||||
Type type = t.getActualTypeArguments()[0];
|
||||
if (type instanceof ParameterizedType) {
|
||||
this.fieldIsMap = ((ParameterizedType) type).getRawType() == Map.class;
|
||||
} else {
|
||||
this.fieldIsMap = false;
|
||||
}
|
||||
} else {
|
||||
this.fieldIsMap = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public Field getThisField() {
|
||||
if (this.thisField == null) {
|
||||
@ -240,4 +267,32 @@ public class MPJTableFieldInfo {
|
||||
this.joinField.getName() + " , " + o.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void bind(MPJTableFieldInfo fieldInfo, T i, List<?> data) {
|
||||
if (!fieldInfo.isCollection()) {
|
||||
if (data.size() > 1 && fieldInfo.isThrowExp()) {
|
||||
throw new MPJException("Expected one result (or null) to be returned by select, but found: " +
|
||||
data.size() + " , " + fieldInfo.getField().getName());
|
||||
} else {
|
||||
fieldInfo.fieldSet(i, data.stream().findFirst().orElse(null));
|
||||
}
|
||||
} else {
|
||||
fieldInfo.fieldSet(i, data);
|
||||
}
|
||||
}
|
||||
|
||||
public static void bindMap(MPJTableFieldInfo fieldInfo, Map<String, Object> i, List<?> data) {
|
||||
if (!fieldInfo.isCollection()) {
|
||||
if (data.size() > 1 && fieldInfo.isThrowExp()) {
|
||||
throw new MPJException("Expected one result (or null) to be returned by select, but found: " +
|
||||
data.size() + " , " + fieldInfo.getField().getName());
|
||||
} else {
|
||||
i.put(fieldInfo.getField().getName(), data.stream().findFirst().orElse(null));
|
||||
}
|
||||
} else {
|
||||
i.put(fieldInfo.getField().getName(), data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -19,13 +19,6 @@ public @interface MPJMapping {
|
||||
*/
|
||||
Class<?> tag();
|
||||
|
||||
/**
|
||||
* 当前类的属性数据结构 是否是Map 或 List<Map>
|
||||
* 如果是 true 关联查询会调用 getMap() / listMaps() 等Map相关方法进行匹配
|
||||
* 如果是 false 关联查询会调用 getOne() / getById() / list() 等实体类相关方法进行匹配
|
||||
*/
|
||||
boolean isMap() default false;
|
||||
|
||||
/**
|
||||
* 当前类的关联的字段名称 (是实体类字段名称而不是数据库字段名称)
|
||||
* 默认获取当前类上定义的主键 @TableId
|
||||
@ -38,6 +31,20 @@ public @interface MPJMapping {
|
||||
*/
|
||||
String joinField() default "";
|
||||
|
||||
/**
|
||||
* 当前类的属性数据结构 是否是Map 或 List<Map>
|
||||
* 如果是 true 关联查询会调用 getMap() / listMaps() 等Map相关方法进行匹配
|
||||
* 如果是 false 关联查询会调用 getOne() / getById() / list() 等实体类相关方法进行匹配
|
||||
*/
|
||||
boolean isMap() default false;
|
||||
|
||||
/**
|
||||
* 一对一查询时 如果查询到多条记录是否抛出异常
|
||||
* true 抛出异常
|
||||
* false 不抛异常,获取列表第一条数据
|
||||
*/
|
||||
boolean isThrowExp() default true;
|
||||
|
||||
/**
|
||||
* 针对查询结果map的时候使用
|
||||
* 默认为thisField对应的数据库列名
|
||||
@ -82,6 +89,7 @@ public @interface MPJMapping {
|
||||
|
||||
/**
|
||||
* 映射表查询条件之 last
|
||||
* 建议不要在这使用分页语句,会导致关联查的时候查询不全
|
||||
* 等效于 Wrappers.<T>query().last(xxx);
|
||||
*/
|
||||
String last() default "";
|
||||
|
@ -17,5 +17,5 @@ public @interface MPJMappingApply {
|
||||
/**
|
||||
* .apply() 对应的可变参数
|
||||
*/
|
||||
String[] args();
|
||||
String[] args() default {};
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 深度查询
|
||||
@ -98,9 +97,9 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
||||
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
|
||||
Object o = map.get(fieldInfo.getThisMapKey());
|
||||
if (o != null) {
|
||||
map.put(fieldInfo.getField().getName(), fieldInfo.getJoinMapper()
|
||||
.mappingWrapperConstructor(fieldInfo.isCollection(), fieldInfo.isFieldIsMap(), SqlKeyword.EQ,
|
||||
fieldInfo.getJoinColumn(), o, fieldInfo));
|
||||
List<?> data = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(fieldInfo.isFieldIsMap(),
|
||||
SqlKeyword.EQ, fieldInfo.getJoinColumn(), o, fieldInfo);
|
||||
MPJTableFieldInfo.bindMap(fieldInfo, map, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,9 +217,9 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
||||
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
|
||||
Object get = fieldInfo.thisFieldGet(t);
|
||||
if (get != null) {
|
||||
fieldInfo.fieldSet(t, fieldInfo.getJoinMapper().mappingWrapperConstructor(fieldInfo.isCollection(),
|
||||
fieldInfo.isFieldIsMap(), SqlKeyword.EQ, fieldInfo.getJoinColumn(),
|
||||
get, fieldInfo));
|
||||
List<?> o = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(fieldInfo.isFieldIsMap(),
|
||||
SqlKeyword.EQ, fieldInfo.getJoinColumn(), get, fieldInfo);
|
||||
MPJTableFieldInfo.bind(fieldInfo, t, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,13 +242,12 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
||||
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
|
||||
List<Object> itemList = list.stream().map(fieldInfo::thisFieldGet).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(itemList)) {
|
||||
List<?> joinList = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(true,
|
||||
List<?> joinList = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(
|
||||
fieldInfo.isFieldIsMap(), SqlKeyword.IN, fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
||||
list.forEach(i -> {
|
||||
Stream<?> stream = joinList.stream().filter(j ->
|
||||
fieldInfo.joinFieldGet(j).equals(fieldInfo.thisFieldGet(i)));
|
||||
fieldInfo.fieldSet(i, fieldInfo.isCollection() ? stream.collect(Collectors.toList()) :
|
||||
stream.findFirst().orElse(null));
|
||||
List<?> data = joinList.stream().filter(j -> fieldInfo.joinFieldGet(j)
|
||||
.equals(fieldInfo.thisFieldGet(i))).collect(Collectors.toList());
|
||||
MPJTableFieldInfo.bind(fieldInfo, i, data);
|
||||
});
|
||||
} else {
|
||||
list.forEach(i -> fieldInfo.fieldSet(i, new ArrayList<>()));
|
||||
@ -259,6 +257,7 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询映射关系
|
||||
* 对结果进行二次查询
|
||||
@ -277,24 +276,23 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
||||
List<Object> itemList = list.stream().map(m -> m.get(fieldInfo.getThisMapKey())).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(itemList)) {
|
||||
if (fieldInfo.isFieldIsMap()) {
|
||||
List<Map<String, Object>> joinList = (List<Map<String, Object>>) fieldInfo.getJoinMapper().mappingWrapperConstructor(true,
|
||||
fieldInfo.isFieldIsMap(), SqlKeyword.IN, fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
||||
List<Map<String, Object>> joinList = (List<Map<String, Object>>) fieldInfo.getJoinMapper()
|
||||
.mappingWrapperConstructor(fieldInfo.isFieldIsMap(), SqlKeyword.IN,
|
||||
fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
||||
list.forEach(i -> {
|
||||
Stream<Map<String, Object>> stream = joinList.stream().filter(j -> j.containsKey(fieldInfo.getJoinMapKey())
|
||||
&& j.get(fieldInfo.getJoinMapKey()).equals(i.get(fieldInfo.getThisMapKey())));
|
||||
i.put(fieldInfo.getField().getName(), fieldInfo.isCollection() ? stream.collect(Collectors.toList()) :
|
||||
stream.findFirst().orElse(null));
|
||||
List<Map<String, Object>> data = joinList.stream().filter(j -> j.containsKey(fieldInfo.getJoinMapKey())
|
||||
&& j.get(fieldInfo.getJoinMapKey()).equals(i.get(fieldInfo.getThisMapKey()))).collect(Collectors.toList());
|
||||
MPJTableFieldInfo.bindMap(fieldInfo, i, data);
|
||||
});
|
||||
} else {
|
||||
List<?> joinList = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(true,
|
||||
List<?> joinList = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(
|
||||
fieldInfo.isFieldIsMap(), SqlKeyword.IN, fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
||||
list.forEach(i -> {
|
||||
Stream<?> stream = joinList.stream().filter(j -> {
|
||||
List<?> data = joinList.stream().filter(j -> {
|
||||
Object o = fieldInfo.joinFieldGet(j);
|
||||
return o != null && o.equals(i.get(fieldInfo.getThisMapKey()));
|
||||
});
|
||||
i.put(fieldInfo.getField().getName(), fieldInfo.isCollection() ? stream.collect(Collectors.toList()) :
|
||||
stream.findFirst().orElse(null));
|
||||
}).collect(Collectors.toList());
|
||||
MPJTableFieldInfo.bindMap(fieldInfo, i, data);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -304,4 +302,6 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ public interface MPJBaseMapper<T> extends BaseMapper<T> {
|
||||
* 映射 wrapper 构造器
|
||||
* 仅对使用 @MPJMapping 时使用
|
||||
*/
|
||||
default Object mappingWrapperConstructor(boolean isCollection, boolean selectMap, SqlKeyword keyword,
|
||||
default Object mappingWrapperConstructor(boolean selectMap, SqlKeyword keyword,
|
||||
String column, Object val, MPJTableFieldInfo fieldInfo) {
|
||||
MPJMappingWrapper infoWrapper = fieldInfo.getWrapper();
|
||||
MappingQuery<T> wrapper = new MappingQuery<>();
|
||||
@ -103,12 +103,10 @@ public interface MPJBaseMapper<T> extends BaseMapper<T> {
|
||||
if (infoWrapper.isHasApply()) {
|
||||
infoWrapper.getApplyList().forEach(a -> wrapper.apply(a.getSql(), (Object[]) a.getVal()));
|
||||
}
|
||||
|
||||
|
||||
if (selectMap) {
|
||||
return isCollection ? selectMaps(wrapper) : selectMaps(wrapper).stream().findFirst().orElse(null);
|
||||
return selectMaps(wrapper);
|
||||
}
|
||||
return isCollection ? selectList(wrapper) : selectOne(wrapper);
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user