feat: add fill

This commit is contained in:
yulichang 2024-10-01 19:06:43 +08:00
parent 52a0eb3cc9
commit 27444dda74
14 changed files with 370 additions and 22 deletions

View File

@ -57,35 +57,34 @@ public class MPJInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object ew = null;
Object[] args = invocation.getArgs();
if (args[0] instanceof MappedStatement) {
MappedStatement ms = (MappedStatement) args[0];
if (args[1] instanceof Map) {
Map<String, Object> map = (Map<String, Object>) args[1];
Object ew = map.getOrDefault(Constants.WRAPPER, null);
ew = map.getOrDefault(Constants.WRAPPER, null);
if (Objects.nonNull(ew) && ew instanceof MPJBaseJoin) {
if (CollectionUtils.isNotEmpty(map)) {
Class<?> rt = null;
if (map.containsKey(Constant.CLAZZ) && map.get(Constant.CLAZZ) != null) {
rt = (Class<?>) map.get(Constant.CLAZZ);
} else {
if (CollectionUtils.isNotEmpty(ms.getResultMaps())) {
Class<?> entity = MPJTableMapperHelper.getEntity(getMapper(ms.getId(), ms.getResource()));
Class<?> type = ms.getResultMaps().get(0).getType();
if (Objects.nonNull(entity) && Objects.nonNull(type)
&& !MPJReflectionKit.isPrimitiveOrWrapper(type) && (entity == type)) {
rt = type;
}
Class<?> rt = null;
if (map.containsKey(Constant.CLAZZ) && map.get(Constant.CLAZZ) != null) {
rt = (Class<?>) map.get(Constant.CLAZZ);
} else {
if (CollectionUtils.isNotEmpty(ms.getResultMaps())) {
Class<?> entity = MPJTableMapperHelper.getEntity(getMapper(ms.getId(), ms.getResource()));
Class<?> type = ms.getResultMaps().get(0).getType();
if (Objects.nonNull(entity) && Objects.nonNull(type)
&& !MPJReflectionKit.isPrimitiveOrWrapper(type) && (entity == type)) {
rt = type;
}
}
if (Objects.nonNull(rt)) {
args[0] = getMappedStatement(ms, rt, ew, map);
}
}
if (Objects.nonNull(rt)) {
args[0] = getMappedStatement(ms, rt, ew, map);
}
}
}
}
return invocation.proceed();
return fill(ew, invocation.proceed());
}
@ -362,6 +361,24 @@ public class MPJInterceptor implements Interceptor {
}
}
private Object fill(Object ew, Object proceed) {
if (Objects.isNull(ew) || !(ew instanceof SelectWrapper<?, ?>) || MPJReflectionKit.isPrimitiveOrWrapper(proceed.getClass())) {
return proceed;
}
if (proceed instanceof List) {
List<?> list = (List<?>) proceed;
if (!list.isEmpty()) {
Object o = list.stream().filter(Objects::nonNull).findFirst().orElse(null);
if (o == null || MPJReflectionKit.isPrimitiveOrWrapper(o.getClass())) {
return proceed;
}
}
}
SelectWrapper<?, ?> selectWrapper = (SelectWrapper<?, ?>) ew;
selectWrapper.doFill(proceed);
return proceed;
}
private Class<?> getMapper(String id, String resource) {
Class<?> clazz = MS_MAPPER_CACHE.computeIfAbsent(id, key -> {
try {

View File

@ -0,0 +1,162 @@
package com.github.yulichang.toolkit;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.toolkit.support.FieldCache;
import com.github.yulichang.toolkit.support.LambdaMeta;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.github.yulichang.wrapper.interfaces.MConsumer;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
/**
* 填充
*
* @auther yulichang
* @since 1.5.1
*/
@SuppressWarnings({"unchecked", "unused"})
public final class FillUtils {
public static void fill(Object data, SFunction<?, ?> field, SFunction<?, ?> tagField) {
fill(data, field, tagField, (MConsumer<MPJLambdaWrapper<?>>) null);
}
public static void fill(Object data, SFunction<?, ?> field, SFunction<?, ?> tagField, MConsumer<MPJLambdaWrapper<?>> consumer) {
LambdaMeta meta = LambdaUtils.getMeta(tagField);
FieldCache fieldCache = MPJReflectionKit.getFieldMap(meta.getInstantiatedClass()).get(meta.getName());
fill(data, field, getTagClass(fieldCache.getField()), tagField, consumer);
}
public static void fill(Object data, SFunction<?, ?> field, Class<?> oneClass, SFunction<?, ?> tagField) {
fill(data, field, oneClass, tagField, null);
}
public static void fill(Object data, SFunction<?, ?> field, Class<?> oneClass, SFunction<?, ?> tagField, MConsumer<MPJLambdaWrapper<?>> consumer) {
TableInfo tableInfo = TableHelper.getAssert(oneClass);
Assert.notNull(tableInfo.getKeyProperty(), "not found key property by class %s", oneClass.getName());
fill(data, field, new SF(oneClass, tableInfo.getKeyProperty()), tagField, consumer);
}
public static void fill(Object data, SFunction<?, ?> field, SFunction<?, ?> oneField, SFunction<?, ?> tagField) {
fill(data, field, oneField, tagField, null);
}
public static void fill(Object data, SFunction<?, ?> field, SFunction<?, ?> oneField, SFunction<?, ?> tagField, MConsumer<MPJLambdaWrapper<?>> consumer) {
doFill(data, field, oneField, tagField, consumer);
}
private static <W> void doFill(Object data, SFunction<?, ?> field, SFunction<?, ?> oneField, SFunction<?, ?> tagField, MConsumer<MPJLambdaWrapper<?>> consumer) {
if (data == null || field == null || oneField == null || tagField == null) {
return;
}
if (data instanceof IPage) {
doFill(((IPage<?>) data).getRecords(), field, oneField, tagField, consumer);
return;
}
if (data instanceof Collection) {
Collection<?> collection = (Collection<?>) data;
if (collection.isEmpty() || collection.stream().allMatch(Objects::isNull)) {
return;
}
}
LambdaMeta sourceMeta = LambdaUtils.getMeta(field);
FieldCache sourceCache = MPJReflectionKit.getFieldMap(sourceMeta.getInstantiatedClass()).get(sourceMeta.getName());
LambdaMeta tagMeta = LambdaUtils.getMeta(tagField);
FieldCache tagCache = MPJReflectionKit.getFieldMap(tagMeta.getInstantiatedClass()).get(tagMeta.getName());
FieldCache oneCache;
if (oneField instanceof SF) {
SF sf = (SF) oneField;
oneCache = MPJReflectionKit.getFieldMap(sf.getClazz()).get(sf.getName());
} else {
LambdaMeta oneMeta = LambdaUtils.getMeta(oneField);
oneCache = MPJReflectionKit.getFieldMap(oneMeta.getInstantiatedClass()).get(oneMeta.getName());
}
Class<?> wrapperClass = (oneField instanceof SF) ? ((SF) oneField).clazz : LambdaUtils.getEntityClass(oneField);
MPJLambdaWrapper<W> wrapper = new MPJLambdaWrapper<W>((Class<W>) wrapperClass) {
@Override
public <R> MPJLambdaWrapper<W> in(SFunction<R, ?> column, Collection<?> coll) {
if (column instanceof SF) {
SF sf = (SF) column;
TableInfo tableInfo = TableHelper.getAssert(sf.getClazz());
return super.in(alias + StringPool.DOT + tableInfo.getKeyColumn(), coll);
}
return super.in(column, coll);
}
@Override
public <R> MPJLambdaWrapper<W> eq(SFunction<R, ?> column, Object val) {
if (column instanceof SF) {
SF sf = (SF) column;
TableInfo tableInfo = TableHelper.getAssert(sf.getClazz());
return super.eq(alias + StringPool.DOT + tableInfo.getKeyColumn(), val);
}
return super.eq(column, val);
}
};
boolean many = Collection.class.isAssignableFrom(tagCache.getType());
if (data instanceof Collection) {
List<?> collection = ((Collection<?>) data).stream().filter(Objects::nonNull).collect(Collectors.toList());
Set<Object> sourceSet = collection.stream().map(sourceCache::getFieldValue).collect(Collectors.toSet());
if (!sourceSet.isEmpty()) {
wrapper.in(oneField, sourceSet);
if (consumer != null) {
consumer.accept(wrapper);
}
List<?> list = wrapper.list();
Map<Object, ?> map;
if (many)
map = list.stream().collect(Collectors.groupingBy(oneCache::getFieldValue));
else
map = list.stream().collect(Collectors.toMap(oneCache::getFieldValue, i -> i));
//匹配
collection.forEach(item -> tagCache.setFieldValue(item, map.get(sourceCache.getFieldValue(item))));
}
} else {
Object sourceVal = sourceCache.getFieldValue(data);
wrapper.eq(oneField, sourceVal);
if (consumer != null) {
consumer.accept(wrapper);
}
Object x = many ? wrapper.list(tagCache.getType()) : wrapper.one(tagCache.getType());
if (x != null) {
tagCache.setFieldValue(data, x);
}
}
}
private static <X> Class<X> getTagClass(Field field) {
if (Collection.class.isAssignableFrom(field.getType())) {
return (Class<X>) MPJReflectionKit.getGenericType(field);
} else {
return (Class<X>) field.getType();
}
}
@Getter
@AllArgsConstructor
private static class SF implements SFunction<Object, Object> {
private Class<?> clazz;
private String name;
@Override
public Object apply(Object o) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -28,6 +28,10 @@ public final class LambdaUtils {
return (Class<T>) extract(fn).getInstantiatedClass();
}
public static <T> LambdaMeta getMeta(SFunction<T, ?> fn) {
return extract(fn);
}
/**
* 该缓存可能会在任意不定的时间被清除
*
@ -35,7 +39,7 @@ public final class LambdaUtils {
* @param <T> 类型被调用的 Function 对象的目标类型
* @return 返回解析后的结果
*/
public static <T> LambdaMeta extract(SFunction<T, ?> func) {
private static <T> LambdaMeta extract(SFunction<T, ?> func) {
// 1. IDEA 调试模式下 lambda 表达式是一个代理
if (func instanceof Proxy) {
return new IdeaProxyLambdaMeta((Proxy) func);

View File

@ -1,5 +1,6 @@
package com.github.yulichang.toolkit.support;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import lombok.Data;
import java.lang.reflect.Field;
@ -16,4 +17,30 @@ public class FieldCache {
public Field field;
public Class<?> type;
public boolean isAccessible = false;
public Object getFieldValue(Object object) {
try {
if (!isAccessible) {
isAccessible = true;
field.setAccessible(true);
}
return field.get(object);
} catch (IllegalAccessException e) {
throw ExceptionUtils.mpe(e);
}
}
public void setFieldValue(Object source, Object value) {
try {
if (!isAccessible) {
isAccessible = true;
field.setAccessible(true);
}
field.set(source, value);
} catch (IllegalAccessException e) {
throw ExceptionUtils.mpe(e);
}
}
}

View File

@ -15,6 +15,8 @@
*/
package com.github.yulichang.toolkit.support;
import org.apache.ibatis.reflection.property.PropertyNamer;
/**
* Lambda 信息
* <p>
@ -22,6 +24,10 @@ package com.github.yulichang.toolkit.support;
*/
public interface LambdaMeta {
default String getName() {
return PropertyNamer.methodToProperty(getImplMethodName());
}
/**
* 获取 lambda 表达式实现方法的名称
*

View File

@ -253,7 +253,6 @@ public abstract class JoinAbstractLambdaWrapper<T, Children extends JoinAbstract
return decode;
}
@Override
@SafeVarargs
protected final <X> String columnsToString(Integer index, PrefixEnum prefixEnum, String alias, SFunction<X, ?>... columns) {

View File

@ -27,7 +27,7 @@ import java.util.stream.Collectors;
* @author yulichang
*/
@SuppressWarnings({"unused", "DuplicatedCode"})
public class MPJLambdaWrapper<T> extends JoinAbstractLambdaWrapper<T, MPJLambdaWrapper<T>> implements
public class MPJLambdaWrapper<T> extends JoinAbstractLambdaWrapper<T, MPJLambdaWrapper<T>> implements Fill<MPJLambdaWrapper<T>>,
Query<MPJLambdaWrapper<T>>, QueryLabel<MPJLambdaWrapper<T>>, Chain<T>, SelectWrapper<T, MPJLambdaWrapper<T>> {
/**
@ -65,6 +65,8 @@ public class MPJLambdaWrapper<T> extends JoinAbstractLambdaWrapper<T, MPJLambdaW
@Getter
private Map<String, Wrapper<?>> wrapperMap;
private List<MConsumer<Object>> fill;
/**
* 推荐使用 class 的构造方法
*/
@ -282,6 +284,21 @@ public class MPJLambdaWrapper<T> extends JoinAbstractLambdaWrapper<T, MPJLambdaW
return typedThis;
}
@Override
public MPJLambdaWrapper<T> addFill(MConsumer<Object> consumer) {
if (this.fill == null) {
this.fill = new ArrayList<>();
}
this.fill.add(consumer);
return typedThis;
}
@Override
public void doFill(Object obj) {
if (this.fill != null) {
this.fill.forEach(c -> c.accept(obj));
}
}
/**
* union
@ -397,5 +414,6 @@ public class MPJLambdaWrapper<T> extends JoinAbstractLambdaWrapper<T, MPJLambdaW
if (Objects.nonNull(unionSql)) unionSql.toEmpty();
resultMapMybatisLabel.clear();
ifExists = ConfigProperties.ifExists;
fill = null;
}
}

View File

@ -0,0 +1,42 @@
package com.github.yulichang.wrapper.interfaces;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.toolkit.FillUtils;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
/**
* 填充
*
* @auther yulichang
* @since 1.5.1
*/
@SuppressWarnings({"unchecked", "unused", "UnusedReturnValue"})
public interface Fill<Children> {
Children addFill(MConsumer<Object> consumer);
default <R> Children fill(SFunction<R, ?> field, SFunction<R, ?> tagField) {
return addFill(c -> FillUtils.fill(c, field, tagField));
}
default <T, X> Children fill(SFunction<T, ?> field, SFunction<T, X> tagField, MConsumer<MPJLambdaWrapper<X>> consumer) {
return addFill(c -> FillUtils.fill(c, field, tagField, (MConsumer<MPJLambdaWrapper<?>>) ((Object) consumer)));
}
default <T, X> Children fill(SFunction<T, ?> field, Class<X> oneClass, SFunction<T, ?> tagField) {
return addFill(c -> FillUtils.fill(c, field, oneClass, tagField));
}
default <T, X> Children fill(SFunction<T, ?> field, Class<X> oneClass, SFunction<T, ?> tagField, MConsumer<MPJLambdaWrapper<X>> consumer) {
return addFill(c -> FillUtils.fill(c, field, oneClass, tagField, (MConsumer<MPJLambdaWrapper<?>>) ((Object) consumer)));
}
default <T, X> Children fill(SFunction<T, ?> field, SFunction<X, ?> oneField, SFunction<T, ?> tagField) {
return addFill(c -> FillUtils.fill(c, field, oneField, tagField));
}
default <T, X> Children fill(SFunction<T, ?> field, SFunction<X, ?> oneField, SFunction<T, ?> tagField, MConsumer<MPJLambdaWrapper<X>> consumer) {
return addFill(c -> FillUtils.fill(c, field, oneField, tagField, (MConsumer<MPJLambdaWrapper<?>>) ((Object) consumer)));
}
}

View File

@ -7,7 +7,7 @@ import java.util.function.BiPredicate;
* on function
*
* @author yulichang
* @since 1.4.14
* @since 1.5.0
*/
@FunctionalInterface
public interface MBiPredicate<T, U> extends BiPredicate<T, U>, Serializable {

View File

@ -0,0 +1,13 @@
package com.github.yulichang.wrapper.interfaces;
import java.io.Serializable;
import java.util.function.Consumer;
/**
* Serializable Consumer
*
* @auther yulichang
* @since 1.5.1
*/
public interface MConsumer<T> extends Consumer<T>, Serializable {
}

View File

@ -7,7 +7,7 @@ import java.util.function.Predicate;
* on function
*
* @author yulichang
* @since 1.4.14
* @since 1.5.0
*/
@FunctionalInterface
public interface MPredicate<T> extends Predicate<T>, Serializable {

View File

@ -33,4 +33,7 @@ public interface SelectWrapper<Entity, Children> {
PageInfo getPageInfo();
boolean isPageByMain();
default void doFill(Object obj) {
}
}

View File

@ -69,6 +69,9 @@ public class UserDO extends ID<Integer> implements Serializable {
@TableField(exist = false)
private List<AddressDO> addressList;
@TableField(exist = false)
private AddressDO address1;
@TableField(exist = false)
private List<AddressDO> addressList2;
}

View File

@ -0,0 +1,54 @@
package com.github.yulichang.test.join.unit;
import com.github.yulichang.test.join.entity.UserDO;
import com.github.yulichang.test.util.Reset;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SuppressWarnings("OptionalGetWithoutIsPresent")
@SpringBootTest
public class FillTest {
@BeforeEach
void setUp() {
Reset.reset();
}
@Test
void fill() {
MPJLambdaWrapper<UserDO> wrapper = JoinWrappers.lambda(UserDO.class).selectAll(UserDO.class);
wrapper.fill(UserDO::getAddressId, UserDO::getAddressList);
List<UserDO> list = wrapper.clone().list();
list.forEach(System.out::println);
assert !list.stream().findFirst().get().getAddressList().isEmpty();
}
@Test
void fill1() {
MPJLambdaWrapper<UserDO> wrapper = JoinWrappers.lambda(UserDO.class).selectAll(UserDO.class)
.eq(UserDO::getId, 1);
wrapper.fill(UserDO::getAddressId, UserDO::getAddress1, w -> {
System.out.println(1);
// w.last("limit 1");
});
UserDO list = wrapper.clone().one();
System.out.println(list);
assert list.getAddress1() != null;
}
}