注解映射优化

This commit is contained in:
yulichang 2023-05-09 14:42:37 +08:00
parent c5573a39cd
commit 679c647253
9 changed files with 145 additions and 86 deletions

View File

@ -41,10 +41,11 @@ public @interface EntityMapping {
boolean isThrowExp() default true;
/**
* 注意!!! 属性名
* 映射表查询条件之 select<br/>
* 等效于 Wrappers.<T>query().select(xxx);
*/
String select() default "";
String[] select() default "";
/**
* 映射表查询条件之 first<br/>

View File

@ -1,13 +0,0 @@
package com.github.yulichang.exception;
/**
* mpj 异常
*
* @author yulichang
*/
public class MPJException extends RuntimeException {
public MPJException(String msg) {
super(msg);
}
}

View File

@ -1,5 +1,6 @@
package com.github.yulichang.extension.mapping.config;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.config.ConfigProperties;
import lombok.Getter;
@ -24,7 +25,7 @@ public class DeepConfig<T> {
@Getter
@Setter
private List<SFunction<T, ?>> prop;
private List<SFunction<T, ?>> property;
@Getter
@Setter
@ -32,7 +33,11 @@ public class DeepConfig<T> {
@Getter
@Setter
private int maxCount = ConfigProperties.mappingMaxCount;
private int deep = ConfigProperties.mappingMaxCount;
@Getter
@Setter
private int maxDeep = ConfigProperties.mappingMaxCount;
public static <T> Builder<T> builder() {
return new Builder<>();
@ -43,16 +48,17 @@ public class DeepConfig<T> {
return (DeepConfig<T>) defaultConfig;
}
@SuppressWarnings("unused")
public static class Builder<T> {
private final DeepConfig<T> conf = new DeepConfig<>();
@SafeVarargs
public final Builder<T> prop(SFunction<T, ?>... prop) {
if (Objects.isNull(conf.prop)) {
conf.prop = new ArrayList<>();
public final Builder<T> property(SFunction<T, ?>... prop) {
if (Objects.isNull(conf.property)) {
conf.property = new ArrayList<>();
}
conf.prop.addAll(Arrays.asList(prop));
conf.property.addAll(Arrays.asList(prop));
return this;
}
@ -61,8 +67,17 @@ public class DeepConfig<T> {
return this;
}
public Builder<T> maxCount(int maxCount) {
conf.maxCount = maxCount;
public Builder<T> deep(int deep) {
Assert.isTrue(deep > 0, "查询深度必须大于0");
conf.deep = deep;
if (deep > conf.maxDeep) {
conf.maxDeep = deep;
}
return this;
}
public Builder<T> maxDeep(int maxDeep) {
conf.maxDeep = maxDeep;
return this;
}

View File

@ -6,9 +6,10 @@ import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.github.yulichang.annotation.EntityMapping;
import com.github.yulichang.annotation.FieldMapping;
import com.github.yulichang.exception.MPJException;
import com.github.yulichang.toolkit.SpringContentUtils;
import com.github.yulichang.toolkit.TableHelper;
import com.github.yulichang.toolkit.support.ColumnCache;
import com.github.yulichang.wrapper.segments.SelectCache;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@ -16,10 +17,8 @@ import lombok.ToString;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
* 字段属性
@ -119,10 +118,12 @@ public class MPJTableFieldInfo {
this.isThrowExp = mapping.isThrowExp();
initThisField(mapping.thisField());
initJoinField(mapping.joinField());
this.isRemoveBindField = StringUtils.isNotBlank(mapping.select()) && (!Arrays.asList(mapping.select().split(
StringPool.COMMA)).contains(this.joinColumn.trim()));
this.wrapper = new MPJMappingWrapper(mapping.first(), StringUtils.isBlank(mapping.select()) ? null :
(this.isRemoveBindField ? this.joinColumn + StringPool.COMMA + mapping.select() : mapping.select()),
this.isRemoveBindField = checkArr(mapping.select()) &&
(!Arrays.asList(mapping.select()).contains(this.joinProperty.trim()) &&
!Arrays.asList(mapping.select()).contains(this.joinColumn.trim()));
this.wrapper = new MPJMappingWrapper(mapping.first(), checkArr(mapping.select()) ?
(this.isRemoveBindField ? propToColumn(this.joinClass, mapping.select(), this.joinColumn) :
propToColumn(this.joinClass, mapping.select(), null)) : null,
mapping.apply(), mapping.condition(), mapping.last(), mapping.orderByAsc(), mapping.orderByDesc());
}
@ -135,15 +136,17 @@ public class MPJTableFieldInfo {
this.property = field.getName();
this.isCollection = Collection.class.isAssignableFrom(field.getType());
if (this.isCollection && !List.class.isAssignableFrom(this.field.getType())) {
throw new MPJException("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + this.field.getType().getTypeName());
throw ExceptionUtils.mpe("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + this.field.getType().getTypeName());
}
this.joinClass = mappingField.tag();
this.isThrowExp = mappingField.isThrowExp();
initThisField(mappingField.thisField());
initJoinField(mappingField.joinField());
this.isRemoveBindField = !mappingField.select().equals(this.joinColumn.trim());
this.wrapper = new MPJMappingWrapper(mappingField.first(), this.isRemoveBindField ? this.joinColumn +
StringPool.COMMA + mappingField.select() : mappingField.select(), mappingField.apply(),
this.isRemoveBindField = !mappingField.select().equals(this.joinColumn.trim()) &&
!mappingField.select().equals(this.joinProperty.trim());
this.wrapper = new MPJMappingWrapper(mappingField.first(), this.isRemoveBindField ?
propToColumn(this.joinClass, new String[]{mappingField.select()}, this.joinColumn) :
propToColumn(this.joinClass, new String[]{mappingField.select()}, null), mappingField.apply(),
mappingField.condition(), mappingField.last(), mappingField.orderByAsc(), mappingField.orderByDesc());
initBindField(mappingField.select());
}
@ -151,15 +154,25 @@ public class MPJTableFieldInfo {
private void initBindField(String bindName) {
TableInfo info = TableHelper.get(this.joinClass);
Assert.notNull(info, "未注册的实体类 <%s>", this.joinClass.getSimpleName());
Field field = info.getFieldList().stream()
.filter(i -> i.getColumn().equals(bindName))
.map(TableFieldInfo::getField).findFirst().orElse(null);
if (field == null && bindName.equals(info.getKeyColumn())) {
//根据属性名查询
Field field = info.getFieldList().stream().filter(i -> i.getProperty().equals(bindName))
.findFirst().map(TableFieldInfo::getField).orElse(null);
if (field == null && bindName.equals(info.getKeyProperty())) {
field = ReflectionKit.getFieldList(joinClass).stream().filter(f ->
f.getName().equals(info.getKeyProperty())).findFirst().orElse(null);
}
if (field == null) {
throw new MPJException("字段不存在 " + this.joinClass.getName() + " " + bindName);
//根据字段查询
field = info.getFieldList().stream()
.filter(i -> i.getColumn().equals(bindName))
.map(TableFieldInfo::getField).findFirst().orElse(null);
if (field == null && bindName.equals(info.getKeyColumn())) {
field = ReflectionKit.getFieldList(joinClass).stream().filter(f ->
f.getName().equals(info.getKeyProperty())).findFirst().orElse(null);
}
if (field == null) {
throw ExceptionUtils.mpe("字段不存在 " + this.joinClass.getName() + " " + bindName);
}
}
this.bindField = field;
}
@ -226,7 +239,7 @@ public class MPJTableFieldInfo {
this.property = field.getName();
this.isCollection = Collection.class.isAssignableFrom(field.getType());
if (this.isCollection && !List.class.isAssignableFrom(this.field.getType())) {
throw new MPJException("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + this.field.getType().getTypeName());
throw ExceptionUtils.mpe("对多关系的数据结构目前只支持 <List> 暂不支持其他Collection实现 " + this.field.getType().getTypeName());
}
if (Map.class.isAssignableFrom(field.getType())) {
throw ExceptionUtils.mpe("映射查询不支持Map结构 <%s.%s>", this.entityType.getSimpleName(), field.getName());
@ -252,12 +265,38 @@ public class MPJTableFieldInfo {
}
}
private boolean checkArr(String[] arr) {
if (Objects.isNull(arr) || arr.length <= 0) {
return false;
}
return Arrays.stream(arr).anyMatch(StringUtils::isNotBlank);
}
private String propToColumn(Class<?> tag, String[] arr, String joinC) {
Map<String, SelectCache> mapField = ColumnCache.getMapField(tag);
List<String> args = null;
if (checkArr(arr)) {
args = Arrays.stream(arr).filter(StringUtils::isNotBlank).map(c -> {
if (mapField.containsKey(c)) {
return mapField.get(c).getColumn();
}
return c;
}).collect(Collectors.toList());
if (StringUtils.isNotBlank(joinC)) {
if (mapField.containsKey(joinC)) {
args.add(mapField.get(joinC).getColumn());
}
}
}
return Optional.ofNullable(args).map(i -> String.join(StringPool.COMMA, i)).orElse(null);
}
public BaseMapper<?> getJoinMapper() {
if (this.joinMapper == null) {
MPJTableInfo joinTableInfo = MPJTableInfoHelper.getTableInfos().stream().filter(table ->
table.getTableInfo().getEntityType() == this.joinClass).findFirst().orElse(null);
if (joinTableInfo == null) {
throw new MPJException("未注册 mapper " + this.joinClass.getName());
throw ExceptionUtils.mpe("未注册 mapper " + this.joinClass.getName());
}
this.joinMapper = SpringContentUtils.getMapper(joinTableInfo.getEntityClass());
}
@ -267,17 +306,16 @@ public class MPJTableFieldInfo {
private TableInfo getTableInfo(Class<?> clazz) {
TableInfo tableInfo = TableHelper.get(clazz);
if (tableInfo == null) {
throw new MPJException("未注册 mapper " + clazz.getName());
throw ExceptionUtils.mpe("未注册 mapper " + clazz.getName());
}
return tableInfo;
}
public void fieldSet(Object o, Object val) {
try {
this.field.set(o, val);
} catch (Exception e) {
throw new MPJException("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.entityType.getName() +
throw ExceptionUtils.mpe("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.entityType.getName() +
" , " + this.field.getName() + " , " + o.getClass().getName());
}
}
@ -286,7 +324,7 @@ public class MPJTableFieldInfo {
try {
return getThisField().get(o);
} catch (Exception e) {
throw new MPJException("无法获取当前关联字段,请检查关联字段是否匹配 " + this.entityType.getName() + " , " +
throw ExceptionUtils.mpe("无法获取当前关联字段,请检查关联字段是否匹配 " + this.entityType.getName() + " , " +
this.thisField.getName() + " , " + o.getClass().getName());
}
}
@ -295,7 +333,7 @@ public class MPJTableFieldInfo {
try {
return getJoinField().get(o);
} catch (Exception e) {
throw new MPJException("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.joinClass.getName() + " , " +
throw ExceptionUtils.mpe("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.joinClass.getName() + " , " +
this.joinField.getName() + " , " + o.getClass().getName());
}
}
@ -304,7 +342,7 @@ public class MPJTableFieldInfo {
try {
return getBindField().get(o);
} catch (Exception e) {
throw new MPJException("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.joinClass.getName() + " , " +
throw ExceptionUtils.mpe("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.joinClass.getName() + " , " +
this.bindField.getName() + " , " + o.getClass().getName());
}
}
@ -313,7 +351,7 @@ public class MPJTableFieldInfo {
try {
this.joinField.set(o, null);
} catch (Exception e) {
throw new MPJException("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.entityType.getName() +
throw ExceptionUtils.mpe("无法设置关联字段,请检查关联字段数据类型是否匹配 " + this.entityType.getName() +
" , " + this.joinField.getName() + " , " + o.getClass().getName());
}
}
@ -329,7 +367,7 @@ public class MPJTableFieldInfo {
fieldInfo.fieldSet(i, data);
} else {
if (data.size() > 1 && fieldInfo.isThrowExp()) {
throw new MPJException("Expected one result (or null) to be returned by select, but found: " +
throw ExceptionUtils.mpe("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));

View File

@ -4,8 +4,6 @@ import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.config.ConfigProperties;
import com.github.yulichang.extension.mapping.config.DeepConfig;
import com.github.yulichang.extension.mapping.mapper.MPJTableFieldInfo;
import com.github.yulichang.extension.mapping.mapper.MPJTableInfo;
@ -13,7 +11,10 @@ import com.github.yulichang.extension.mapping.mapper.MPJTableInfoHelper;
import com.github.yulichang.extension.mapping.wrapper.MappingQuery;
import com.github.yulichang.toolkit.LambdaUtils;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
@ -28,7 +29,7 @@ public class Relation {
* @see com.github.yulichang.annotation.FieldMapping
*/
public static <R, T> R mpjGetRelation(R r, DeepConfig<T> config) {
int start = ConfigProperties.mappingMaxCount - config.getMaxCount();
int start = 1;
if (Objects.isNull(r)) {
return null;
} else if (r instanceof List) {
@ -43,7 +44,7 @@ public class Relation {
if (Object.class == t.getClass()) {
return r;
}
return (R) Relation.list(data, config.getProp(), config.isLoop(), start);
return (R) Relation.list(data, start, config);
}
} else if (r instanceof IPage) {
IPage<T> data = (IPage<T>) r;
@ -55,7 +56,7 @@ public class Relation {
if (Object.class == t.getClass()) {
return r;
}
Relation.list(data.getRecords(), config.getProp(), config.isLoop(), start);
Relation.list(data.getRecords(), start, config);
}
return r;
} else if (r instanceof Integer) {
@ -67,19 +68,19 @@ public class Relation {
} else if (Object.class == r.getClass()) {
return r;
} else {
return (R) Relation.one((T) r, config.getProp(), config.isLoop(), start);
return (R) Relation.one((T) r, start, config);
}
}
public static <T> List<T> list(List<T> data, List<SFunction<T, ?>> property, boolean loop, int count) {
public static <T> List<T> list(List<T> data, int currDeep, DeepConfig<T> config) {
if (CollectionUtils.isEmpty(data)) {
return data;
}
Class<?> entityClass = data.get(0).getClass();
MPJTableInfo tableInfo = MPJTableInfoHelper.getTableInfo(entityClass);
if (tableInfo.isHasMappingOrField()) {
boolean hasProperty = CollectionUtils.isNotEmpty(property);
List<String> listProperty = hasProperty ? property.stream().map(LambdaUtils::getName).collect(
boolean hasProperty = CollectionUtils.isNotEmpty(config.getProperty());
List<String> listProperty = hasProperty ? config.getProperty().stream().map(LambdaUtils::getName).collect(
Collectors.toList()) : null;
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
if (!hasProperty || listProperty.contains(fieldInfo.getProperty())) {
@ -87,11 +88,8 @@ public class Relation {
if (CollectionUtils.isNotEmpty(itemList)) {
List<?> joinList = MappingQuery.mpjQueryList(fieldInfo.getJoinMapper(), SqlKeyword.IN,
fieldInfo.getJoinColumn(), itemList, fieldInfo);
data.forEach(i -> mpjBindData(i, property, fieldInfo, joinList, loop, count));
data.forEach(i -> mpjBindData(i, fieldInfo, joinList, currDeep, config));
fieldInfo.removeJoinField(joinList);
if (CollectionUtils.isEmpty(joinList)) {
continue;
}
} else {
data.forEach(i -> fieldInfo.fieldSet(i, new ArrayList<>()));
}
@ -110,14 +108,14 @@ public class Relation {
*
* @param t 第一次查询结果
*/
public static <T> T one(T t, List<SFunction<T, ?>> property, boolean loop, int count) {
public static <T> T one(T t, int currDeep, DeepConfig<T> config) {
if (t == null) {
return null;
}
MPJTableInfo tableInfo = MPJTableInfoHelper.getTableInfo(t.getClass());
if (tableInfo.isHasMappingOrField()) {
boolean hasProperty = CollectionUtils.isNotEmpty(property);
List<String> list = hasProperty ? property.stream().map(LambdaUtils::getName).collect(
boolean hasProperty = CollectionUtils.isNotEmpty(config.getProperty());
List<String> list = hasProperty ? config.getProperty().stream().map(LambdaUtils::getName).collect(
Collectors.toList()) : null;
for (MPJTableFieldInfo fieldInfo : tableInfo.getFieldList()) {
if (!hasProperty || list.contains(fieldInfo.getProperty())) {
@ -125,7 +123,7 @@ public class Relation {
if (obj != null) {
List<?> joinList = MappingQuery.mpjQueryList(fieldInfo.getJoinMapper(), SqlKeyword.EQ,
fieldInfo.getJoinColumn(), obj, fieldInfo);
mpjBindData(t, property, fieldInfo, joinList, loop, count);
mpjBindData(t, fieldInfo, joinList, currDeep, config);
fieldInfo.removeJoinField(joinList);
}
}
@ -134,21 +132,28 @@ public class Relation {
return t;
}
public static <R, T, E> void mpjBindData(R t, List<SFunction<T, ?>> property, MPJTableFieldInfo fieldInfo, List<?> joinList, boolean loop, int count) {
public static <R, T, E> void mpjBindData(R t, MPJTableFieldInfo fieldInfo,
List<?> joinList, int currDeep, DeepConfig<T> config) {
if (currDeep >= config.getDeep()) {
return;
}
if (currDeep >= config.getMaxDeep()) {
throw ExceptionUtils.mpe("超过最大查询深度");
}
if (fieldInfo.isMappingEntity()) {
if (count > ConfigProperties.mappingMaxCount) {
throw ExceptionUtils.mpe("超过最大查询深度");
}
List<E> list = (List<E>) joinList.stream().filter(j -> fieldInfo.joinFieldGet(j).equals(fieldInfo.thisFieldGet(t)))
.collect(Collectors.toList());
MPJTableFieldInfo.bind(fieldInfo, t, list);
if (loop && CollectionUtils.isNotEmpty(list)) {
int newCount = count + 1;
if (CollectionUtils.isNotEmpty(property) && LambdaUtils.getEntityClass(property.get(0)).isAssignableFrom(list.get(0).getClass())) {
List<SFunction<E, ?>> property1 = ((List<SFunction<E, ?>>) ((Object) property));
list(list, property1, loop, count);
if (config.isLoop() && CollectionUtils.isNotEmpty(list)) {
int newCount = currDeep + 1;
if (CollectionUtils.isNotEmpty(config.getProperty()) && LambdaUtils.getEntityClass(config.getProperty().get(0)).isAssignableFrom(list.get(0).getClass())) {
list(list, newCount, (DeepConfig<E>) config);
} else {
list(list, Collections.EMPTY_LIST, loop, newCount);
DeepConfig<E> deepConfig = new DeepConfig<>();
deepConfig.setDeep(config.getDeep());
deepConfig.setLoop(config.isLoop());
deepConfig.setMaxDeep(config.getMaxDeep());
list(list, newCount, deepConfig);
}
}
}

View File

@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.github.yulichang.annotation.DynamicTableName;
import com.github.yulichang.annotation.EntityMapping;
import com.github.yulichang.test.join.enums.Sex;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -16,9 +16,7 @@ import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.DeleteJoinWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.github.yulichang.wrapper.UpdateJoinWrapper;
import org.h2.engine.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.BadSqlGrammarException;

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.github.yulichang.annotation.EntityMapping;
import com.github.yulichang.annotation.FieldMapping;
import com.github.yulichang.test.mapping.enums.Sex;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -44,6 +45,7 @@ public class UserDO {
private UserDO pUser;
@TableField(exist = false)
@EntityMapping(thisField = "id", joinField = "pid")
private List<UserDO> userList;
// @EntityMapping(thisField = "id", joinField = "pid")
@FieldMapping(tag = UserDO.class, thisField = "id", joinField = "pid", select = "head_img")
private List<String> pName;
}

View File

@ -23,11 +23,25 @@ class MappingTest {
@Test
public void test() {
List<UserDO> dos = userService.getRelation(m -> m.selectList(new LambdaQueryWrapper<UserDO>()
.eq(UserDO::getPid, 5)), conf -> conf.prop(UserDO::getPUser).loop(true).maxCount(100));
System.out.println(1);
LambdaQueryWrapper<UserDO> wrapper = new LambdaQueryWrapper<UserDO>()
.eq(UserDO::getPid, 5);
List<UserDO> dos = userService.getRelation(m -> m.selectList(wrapper));
assert dos.get(0).getPUser() != null && dos.get(0).getPUser().getPUser() == null;
LambdaQueryWrapper<UserDO> wrapper1 = new LambdaQueryWrapper<UserDO>()
.eq(UserDO::getPid, 5);
List<UserDO> dos1 = userService.getRelation(m -> m.selectList(wrapper1), conf -> conf.loop(true).deep(3));
assert dos1.get(0).getPUser() != null && dos1.get(0).getPUser().getPUser().getPUser() == null;
LambdaQueryWrapper<UserDO> wrapper2 = new LambdaQueryWrapper<UserDO>()
.eq(UserDO::getPid, 5);
List<UserDO> dos2 = userService.getRelation(m -> m.selectList(wrapper2), conf -> conf.loop(true).deep(3).property(UserDO::getPUser));
assert dos2.get(0).getPUser() != null && dos2.get(0).getPUser().getPUser().getPUser() == null;
assert dos2.get(0).getPName() == null && dos2.get(0).getPUser().getPName() == null &&
dos2.get(0).getPUser().getPUser().getPName() == null;
}
@Test
public void testJoin() {
MPJLambdaWrapper<UserDO> wrapper = new MPJLambdaWrapper<UserDO>()