一对一 一对多 关系映射注解实现

This commit is contained in:
bjdys 2021-08-12 11:56:02 +08:00
parent 866bccff28
commit a6c577ee88
6 changed files with 117 additions and 56 deletions

View File

@ -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;
} }
} }

View File

@ -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);
}
}
} }

View File

@ -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 "";

View File

@ -17,5 +17,5 @@ public @interface MPJMappingApply {
/** /**
* .apply() 对应的可变参数 * .apply() 对应的可变参数
*/ */
String[] args(); String[] args() default {};
} }

View File

@ -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;
} }
} }

View File

@ -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);
} }
/** /**