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.MPJMappingApply;
|
||||||
import com.github.yulichang.annotation.MPJMappingCondition;
|
import com.github.yulichang.annotation.MPJMappingCondition;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -18,7 +18,7 @@ import java.util.List;
|
|||||||
* @author yulichang
|
* @author yulichang
|
||||||
* @since 1.2.0
|
* @since 1.2.0
|
||||||
*/
|
*/
|
||||||
@Data
|
@Getter
|
||||||
public class MPJMappingWrapper {
|
public class MPJMappingWrapper {
|
||||||
|
|
||||||
private final boolean hasFirst;
|
private final boolean hasFirst;
|
||||||
@ -69,18 +69,18 @@ public class MPJMappingWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class Apply {
|
public static class Apply {
|
||||||
private String sql;
|
private final String sql;
|
||||||
private String[] val;
|
private final String[] val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class Condition {
|
public static class Condition {
|
||||||
private SqlKeyword keyword;
|
private final SqlKeyword keyword;
|
||||||
private String column;
|
private final String column;
|
||||||
private String[] val;
|
private final String[] val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,12 @@ import lombok.Getter;
|
|||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
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 MPJMappingWrapper wrapper;
|
||||||
|
/**
|
||||||
|
* 一对一查询结果数量不匹配是是否抛出异常
|
||||||
|
*/
|
||||||
|
private final boolean isThrowExp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化关联字段信息
|
* 初始化关联字段信息
|
||||||
*/
|
*/
|
||||||
public MPJTableFieldInfo(Class<?> entityType, MPJMapping mapping, Field field) {
|
public MPJTableFieldInfo(Class<?> entityType, MPJMapping mapping, Field field1) {
|
||||||
field.setAccessible(true);
|
initField(field1);
|
||||||
this.entityType = entityType;
|
this.entityType = entityType;
|
||||||
this.field = field;
|
|
||||||
this.joinClass = mapping.tag();
|
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.thisMapKey = StringUtils.isBlank(mapping.thisMapKey()) ? null : mapping.thisMapKey();
|
||||||
this.joinMapKey = StringUtils.isBlank(mapping.joinMapKsy()) ? null : mapping.joinMapKsy();
|
this.joinMapKey = StringUtils.isBlank(mapping.joinMapKsy()) ? null : mapping.joinMapKsy();
|
||||||
this.fieldIsMap = mapping.isMap();//TODO 应该可以自动检测
|
|
||||||
this.wrapper = new MPJMappingWrapper(mapping);
|
this.wrapper = new MPJMappingWrapper(mapping);
|
||||||
if (this.isCollection && field.getType() != List.class && field.getType() != ArrayList.class) {
|
if (this.isCollection && this.field.getType() != List.class && this.field.getType() != ArrayList.class) {
|
||||||
throw new MPJException("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + field.getType().getTypeName());
|
throw new MPJException("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + this.field.getType().getTypeName());
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(mapping.joinField())) {
|
if (StringUtils.isNotBlank(mapping.joinField())) {
|
||||||
this.joinProperty = 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")
|
@SuppressWarnings("ConstantConditions")
|
||||||
public Field getThisField() {
|
public Field getThisField() {
|
||||||
if (this.thisField == null) {
|
if (this.thisField == null) {
|
||||||
@ -240,4 +267,32 @@ public class MPJTableFieldInfo {
|
|||||||
this.joinField.getName() + " , " + o.getClass().getName());
|
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();
|
Class<?> tag();
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前类的属性数据结构 是否是Map 或 List<Map>
|
|
||||||
* 如果是 true 关联查询会调用 getMap() / listMaps() 等Map相关方法进行匹配
|
|
||||||
* 如果是 false 关联查询会调用 getOne() / getById() / list() 等实体类相关方法进行匹配
|
|
||||||
*/
|
|
||||||
boolean isMap() default false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前类的关联的字段名称 (是实体类字段名称而不是数据库字段名称)
|
* 当前类的关联的字段名称 (是实体类字段名称而不是数据库字段名称)
|
||||||
* 默认获取当前类上定义的主键 @TableId
|
* 默认获取当前类上定义的主键 @TableId
|
||||||
@ -38,6 +31,20 @@ public @interface MPJMapping {
|
|||||||
*/
|
*/
|
||||||
String joinField() default "";
|
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的时候使用
|
* 针对查询结果map的时候使用
|
||||||
* 默认为thisField对应的数据库列名
|
* 默认为thisField对应的数据库列名
|
||||||
@ -82,6 +89,7 @@ public @interface MPJMapping {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 映射表查询条件之 last
|
* 映射表查询条件之 last
|
||||||
|
* 建议不要在这使用分页语句,会导致关联查的时候查询不全
|
||||||
* 等效于 Wrappers.<T>query().last(xxx);
|
* 等效于 Wrappers.<T>query().last(xxx);
|
||||||
*/
|
*/
|
||||||
String last() default "";
|
String last() default "";
|
||||||
|
@ -17,5 +17,5 @@ public @interface MPJMappingApply {
|
|||||||
/**
|
/**
|
||||||
* .apply() 对应的可变参数
|
* .apply() 对应的可变参数
|
||||||
*/
|
*/
|
||||||
String[] args();
|
String[] args() default {};
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
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()) {
|
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
|
||||||
Object o = map.get(fieldInfo.getThisMapKey());
|
Object o = map.get(fieldInfo.getThisMapKey());
|
||||||
if (o != null) {
|
if (o != null) {
|
||||||
map.put(fieldInfo.getField().getName(), fieldInfo.getJoinMapper()
|
List<?> data = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(fieldInfo.isFieldIsMap(),
|
||||||
.mappingWrapperConstructor(fieldInfo.isCollection(), fieldInfo.isFieldIsMap(), SqlKeyword.EQ,
|
SqlKeyword.EQ, fieldInfo.getJoinColumn(), o, fieldInfo);
|
||||||
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()) {
|
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
|
||||||
Object get = fieldInfo.thisFieldGet(t);
|
Object get = fieldInfo.thisFieldGet(t);
|
||||||
if (get != null) {
|
if (get != null) {
|
||||||
fieldInfo.fieldSet(t, fieldInfo.getJoinMapper().mappingWrapperConstructor(fieldInfo.isCollection(),
|
List<?> o = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(fieldInfo.isFieldIsMap(),
|
||||||
fieldInfo.isFieldIsMap(), SqlKeyword.EQ, fieldInfo.getJoinColumn(),
|
SqlKeyword.EQ, fieldInfo.getJoinColumn(), get, fieldInfo);
|
||||||
get, fieldInfo));
|
MPJTableFieldInfo.bind(fieldInfo, t, o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,13 +242,12 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
|||||||
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
|
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
|
||||||
List<Object> itemList = list.stream().map(fieldInfo::thisFieldGet).collect(Collectors.toList());
|
List<Object> itemList = list.stream().map(fieldInfo::thisFieldGet).collect(Collectors.toList());
|
||||||
if (CollectionUtils.isNotEmpty(itemList)) {
|
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);
|
fieldInfo.isFieldIsMap(), SqlKeyword.IN, fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
||||||
list.forEach(i -> {
|
list.forEach(i -> {
|
||||||
Stream<?> stream = joinList.stream().filter(j ->
|
List<?> data = joinList.stream().filter(j -> fieldInfo.joinFieldGet(j)
|
||||||
fieldInfo.joinFieldGet(j).equals(fieldInfo.thisFieldGet(i)));
|
.equals(fieldInfo.thisFieldGet(i))).collect(Collectors.toList());
|
||||||
fieldInfo.fieldSet(i, fieldInfo.isCollection() ? stream.collect(Collectors.toList()) :
|
MPJTableFieldInfo.bind(fieldInfo, i, data);
|
||||||
stream.findFirst().orElse(null));
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
list.forEach(i -> fieldInfo.fieldSet(i, new ArrayList<>()));
|
list.forEach(i -> fieldInfo.fieldSet(i, new ArrayList<>()));
|
||||||
@ -259,6 +257,7 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
|||||||
return list;
|
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());
|
List<Object> itemList = list.stream().map(m -> m.get(fieldInfo.getThisMapKey())).collect(Collectors.toList());
|
||||||
if (CollectionUtils.isNotEmpty(itemList)) {
|
if (CollectionUtils.isNotEmpty(itemList)) {
|
||||||
if (fieldInfo.isFieldIsMap()) {
|
if (fieldInfo.isFieldIsMap()) {
|
||||||
List<Map<String, Object>> joinList = (List<Map<String, Object>>) fieldInfo.getJoinMapper().mappingWrapperConstructor(true,
|
List<Map<String, Object>> joinList = (List<Map<String, Object>>) fieldInfo.getJoinMapper()
|
||||||
fieldInfo.isFieldIsMap(), SqlKeyword.IN, fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
.mappingWrapperConstructor(fieldInfo.isFieldIsMap(), SqlKeyword.IN,
|
||||||
|
fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
||||||
list.forEach(i -> {
|
list.forEach(i -> {
|
||||||
Stream<Map<String, Object>> stream = joinList.stream().filter(j -> j.containsKey(fieldInfo.getJoinMapKey())
|
List<Map<String, Object>> data = joinList.stream().filter(j -> j.containsKey(fieldInfo.getJoinMapKey())
|
||||||
&& j.get(fieldInfo.getJoinMapKey()).equals(i.get(fieldInfo.getThisMapKey())));
|
&& j.get(fieldInfo.getJoinMapKey()).equals(i.get(fieldInfo.getThisMapKey()))).collect(Collectors.toList());
|
||||||
i.put(fieldInfo.getField().getName(), fieldInfo.isCollection() ? stream.collect(Collectors.toList()) :
|
MPJTableFieldInfo.bindMap(fieldInfo, i, data);
|
||||||
stream.findFirst().orElse(null));
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
List<?> joinList = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(true,
|
List<?> joinList = (List<?>) fieldInfo.getJoinMapper().mappingWrapperConstructor(
|
||||||
fieldInfo.isFieldIsMap(), SqlKeyword.IN, fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
fieldInfo.isFieldIsMap(), SqlKeyword.IN, fieldInfo.getJoinColumn(), itemList, fieldInfo);
|
||||||
list.forEach(i -> {
|
list.forEach(i -> {
|
||||||
Stream<?> stream = joinList.stream().filter(j -> {
|
List<?> data = joinList.stream().filter(j -> {
|
||||||
Object o = fieldInfo.joinFieldGet(j);
|
Object o = fieldInfo.joinFieldGet(j);
|
||||||
return o != null && o.equals(i.get(fieldInfo.getThisMapKey()));
|
return o != null && o.equals(i.get(fieldInfo.getThisMapKey()));
|
||||||
});
|
}).collect(Collectors.toList());
|
||||||
i.put(fieldInfo.getField().getName(), fieldInfo.isCollection() ? stream.collect(Collectors.toList()) :
|
MPJTableFieldInfo.bindMap(fieldInfo, i, data);
|
||||||
stream.findFirst().orElse(null));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -304,4 +302,6 @@ public interface MPJBaseDeepService<T> extends IService<T> {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ public interface MPJBaseMapper<T> extends BaseMapper<T> {
|
|||||||
* 映射 wrapper 构造器
|
* 映射 wrapper 构造器
|
||||||
* 仅对使用 @MPJMapping 时使用
|
* 仅对使用 @MPJMapping 时使用
|
||||||
*/
|
*/
|
||||||
default Object mappingWrapperConstructor(boolean isCollection, boolean selectMap, SqlKeyword keyword,
|
default Object mappingWrapperConstructor(boolean selectMap, SqlKeyword keyword,
|
||||||
String column, Object val, MPJTableFieldInfo fieldInfo) {
|
String column, Object val, MPJTableFieldInfo fieldInfo) {
|
||||||
MPJMappingWrapper infoWrapper = fieldInfo.getWrapper();
|
MPJMappingWrapper infoWrapper = fieldInfo.getWrapper();
|
||||||
MappingQuery<T> wrapper = new MappingQuery<>();
|
MappingQuery<T> wrapper = new MappingQuery<>();
|
||||||
@ -103,12 +103,10 @@ public interface MPJBaseMapper<T> extends BaseMapper<T> {
|
|||||||
if (infoWrapper.isHasApply()) {
|
if (infoWrapper.isHasApply()) {
|
||||||
infoWrapper.getApplyList().forEach(a -> wrapper.apply(a.getSql(), (Object[]) a.getVal()));
|
infoWrapper.getApplyList().forEach(a -> wrapper.apply(a.getSql(), (Object[]) a.getVal()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (selectMap) {
|
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