refactor: 优化对拓展实体支持

This commit is contained in:
zhouhao
2024-12-27 19:15:00 +08:00
parent c03c21b2c9
commit 9c6b20172e
11 changed files with 314 additions and 68 deletions

View File

@@ -19,6 +19,14 @@ public final class EntityFactoryHolder {
return FACTORY;
}
public static <T> Class<T> getMappedType(Class<T> type) {
if (FACTORY != null) {
return FACTORY.getInstanceType(type);
}
return type;
}
public static <T> T newInstance(Class<T> type,
Supplier<T> mapper) {
if (FACTORY != null) {

View File

@@ -14,6 +14,7 @@ import org.hswebframework.ezorm.rdb.executor.wrapper.ColumnWrapperContext;
import org.hswebframework.ezorm.rdb.executor.wrapper.MapResultWrapper;
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrapper;
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrappers;
import org.hswebframework.ezorm.rdb.mapping.EntityPropertyDescriptor;
import org.hswebframework.ezorm.rdb.mapping.defaults.record.DefaultRecord;
import org.hswebframework.ezorm.rdb.mapping.defaults.record.Record;
import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata;
@@ -30,6 +31,8 @@ import org.hswebframework.ezorm.rdb.operator.dml.SelectColumnSupplier;
import org.hswebframework.ezorm.rdb.operator.dml.query.BuildParameterQueryOperator;
import org.hswebframework.ezorm.rdb.operator.dml.query.Selects;
import org.hswebframework.ezorm.rdb.operator.dml.query.SortOrder;
import org.hswebframework.ezorm.rdb.utils.PropertyUtils;
import org.hswebframework.web.api.crud.entity.EntityFactoryHolder;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.bean.FastBeanCopier;
@@ -367,6 +370,9 @@ public class DefaultQueryHelper implements QueryHelper {
RDBColumnMetadata metadata = target.getColumn(column[1]).orElse(null);
if (metadata != null) {
if(metadata.getFeature(EntityPropertyDescriptor.ID).isPresent()){
return;
}
ObjectPropertyOperator operator = GlobalConfig.getPropertyOperator();
if (targetProperty == null) {
operator.setProperty(result, column[1], metadata.decode(sqlValue));
@@ -510,7 +516,7 @@ public class DefaultQueryHelper implements QueryHelper {
private Function<Flux<R>, Flux<R>> resultHandler = Function.identity();
public QuerySpec(Class<R> clazz, DefaultQueryHelper parent) {
this.clazz = clazz;
this.clazz = EntityFactoryHolder.getMappedType(clazz);
this.parent = parent;
logContext = Context.of(Logger.class, LoggerFactory.getLogger(clazz));
}
@@ -869,10 +875,15 @@ public class DefaultQueryHelper implements QueryHelper {
return join(type, createJoinAlias(), JoinType.right, on);
}
@SneakyThrows
public R newRowInstance0() {
return clazz.getConstructor().newInstance();
}
@Override
@SneakyThrows
public R newRowInstance() {
return clazz.newInstance();
return EntityFactoryHolder.newInstance(clazz, this::newRowInstance0);
}
@Override

View File

@@ -3,6 +3,9 @@ package org.hswebframework.web.crud.query;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.hswebframework.ezorm.core.FeatureId;
import org.hswebframework.ezorm.core.FeatureType;
import org.hswebframework.ezorm.core.meta.Feature;
import org.hswebframework.ezorm.rdb.executor.SqlRequest;
import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata;
import org.hswebframework.ezorm.rdb.metadata.TableOrViewMetadata;
@@ -129,7 +132,9 @@ public interface QueryAnalyzer {
@AllArgsConstructor
@Getter
class Column {
class Column implements Feature {
static final FeatureId<Column> FEATURE_ID = FeatureId.of("AnalyzedColumn");
//列名
String name;
//别名
@@ -142,6 +147,16 @@ public interface QueryAnalyzer {
public Column moveOwner(String owner) {
return new Column(name, alias, owner, metadata);
}
@Override
public String getId() {
return FEATURE_ID.getId();
}
@Override
public FeatureType getType() {
return AnalyzerFeatureType.AnalyzedCol;
}
}
class SelectTable extends Table {
@@ -176,4 +191,18 @@ public interface QueryAnalyzer {
}
enum AnalyzerFeatureType implements FeatureType {
AnalyzedCol;
@Override
public String getId() {
return name();
}
@Override
public String getName() {
return name();
}
}
}

View File

@@ -1,5 +1,6 @@
package org.hswebframework.web.cache.supports;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.web.cache.ReactiveCache;
import org.reactivestreams.Publisher;
import reactor.core.CoreSubscriber;
@@ -17,8 +18,10 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@Slf4j
public abstract class AbstractReactiveCache<E> implements ReactiveCache<E> {
static Sinks.EmitFailureHandler emitFailureHandler = Sinks.EmitFailureHandler.busyLooping(Duration.ofSeconds(30));
static final Sinks.EmitFailureHandler emitFailureHandler = Sinks.EmitFailureHandler.busyLooping(Duration.ofSeconds(30));
private final Map<Object, CacheLoader> cacheLoading = new ConcurrentHashMap<>();
protected static class CacheLoader extends MonoOperator<Object, Object> {
@@ -53,25 +56,25 @@ public abstract class AbstractReactiveCache<E> implements ReactiveCache<E> {
Mono<? extends Object> source = this.source;
if (defaultValue != null) {
source = source
.switchIfEmpty((Mono) defaultValue
.flatMap(val -> {
return parent.putNow(key, val).thenReturn(val);
}));
.switchIfEmpty((Mono) defaultValue
.flatMap(val -> {
return parent.putNow(key, val).thenReturn(val);
}));
}
loading = source.subscribe(
val -> {
complete();
holder.emitValue(val, emitFailureHandler);
},
err -> {
complete();
holder.emitError(err, emitFailureHandler);
},
() -> {
complete();
holder.emitEmpty(emitFailureHandler);
},
Context.of(context));
val -> {
complete();
holder.emitValue(val, emitFailureHandler);
},
err -> {
complete();
holder.emitError(err, emitFailureHandler);
},
() -> {
complete();
holder.emitEmpty(emitFailureHandler);
},
Context.of(context));
}
}
@@ -95,42 +98,53 @@ public abstract class AbstractReactiveCache<E> implements ReactiveCache<E> {
@Override
@SuppressWarnings("all")
public final Mono<E> getMono(Object key) {
return (Mono<E>) cacheLoading.computeIfAbsent(key, _key -> new CacheLoader(this, _key, getNow(_key)));
return (Mono<E>) cacheLoading
.computeIfAbsent(key, _key -> new CacheLoader(this, _key, getNow(_key)))
.onErrorResume(err -> handleLoaderError(key, err));
}
@Override
@SuppressWarnings("all")
public final Mono<E> getMono(Object key, Supplier<Mono<E>> loader) {
return Mono.deferContextual(ctx -> {
CacheLoader cacheLoader = cacheLoading.compute(key, (_key, old) -> {
CacheLoader cl = new CacheLoader(this, _key, getNow(_key));
cl.defaultValue(loader.get(), ctx);
return cl;
});
return (Mono<E>) cacheLoader;
});
return Mono
.deferContextual(ctx -> {
CacheLoader cacheLoader = cacheLoading.compute(key, (_key, old) -> {
CacheLoader cl = new CacheLoader(this, _key, getNow(_key));
cl.defaultValue(loader.get(), ctx);
return cl;
});
return (Mono<E>) cacheLoader;
})
.onErrorResume(err -> handleLoaderError(key, err));
}
@Override
public final Flux<E> getFlux(Object key) {
return (cacheLoading.computeIfAbsent(key, _key -> new CacheLoader(this, _key, getNow(_key))))
.flatMapIterable(e -> ((List<E>) e));
.flatMapIterable(e -> ((List<E>) e))
.onErrorResume(err -> handleLoaderError(key, err));
}
@Override
public final Flux<E> getFlux(Object key, Supplier<Flux<E>> loader) {
return Flux.deferContextual(ctx -> {
CacheLoader cacheLoader = cacheLoading.compute(key, (_key, old) -> {
CacheLoader cl = new CacheLoader(this, _key, getNow(_key));
cl.defaultValue(loader.get().collectList(), ctx);
return cl;
});
return cacheLoader.flatMapIterable(e -> ((List<E>) e));
});
CacheLoader cacheLoader = cacheLoading.compute(key, (_key, old) -> {
CacheLoader cl = new CacheLoader(this, _key, getNow(_key));
cl.defaultValue(loader.get().collectList(), ctx);
return cl;
});
return cacheLoader.flatMapIterable(e -> ((List<E>) e));
})
.onErrorResume(err -> handleLoaderError(key, err));
}
protected Mono<E> handleLoaderError(Object key, Throwable err) {
log.warn("load cache error,key:{},evict it.", key, err);
return evict(key)
.then(Mono.empty());
}
@Override
public final Mono<Void> put(Object key, Publisher<E> data) {

View File

@@ -137,5 +137,10 @@
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-easy-orm-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -3,7 +3,12 @@ package org.hswebframework.web.bean;
import lombok.Getter;
import org.hswebframework.web.dict.EnumDict;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Getter
public class ClassDescription {
@@ -17,6 +22,7 @@ public class ClassDescription {
private final boolean number;
private final Object[] enums;
private final Map<String, Field> fields;
public ClassDescription(Class<?> type) {
this.type = type;
@@ -31,6 +37,9 @@ public class ClassDescription {
} else {
enums = null;
}
fields = Arrays
.stream(type.getDeclaredFields())
.collect(Collectors.toMap(Field::getName, f -> f, (a, b) -> b));
}
}

View File

@@ -0,0 +1,23 @@
package org.hswebframework.web.bean;
import lombok.AllArgsConstructor;
import org.hswebframework.ezorm.core.Extensible;
import java.util.Map;
import java.util.Set;
@AllArgsConstructor
class ExtensibleToMapCopier implements Copier {
private final Copier copier;
@Override
public void copy(Object source, Object target, Set<String> ignore, Converter converter) {
copier.copy(source, target, ignore, converter);
ExtensibleUtils.copyToMap((Extensible) target, ignore, (Map<String, Object>) source);
//移除map中的extensions
((Map<?, ?>) source).remove("extensions");
}
}

View File

@@ -0,0 +1,41 @@
package org.hswebframework.web.bean;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.core.Extensible;
import java.util.Map;
import java.util.Set;
public class ExtensibleUtils {
public static void copyFromMap(Map<String, Object> source,
Set<String> ignore,
Extensible target) {
ClassDescription def = ClassDescriptions.getDescription(target.getClass());
for (Map.Entry<String, Object> entry : source.entrySet()) {
//只copy没有定义的数据
if (!ignore.contains(entry.getKey()) && !def.getFields().containsKey(entry.getKey())) {
target.setExtension(entry.getKey(), entry.getValue());
}
}
}
public static void copyToMap(Extensible target,
Set<String> ignore,
Map<String, Object> source) {
if (CollectionUtils.isNotEmpty(ignore)) {
source.putAll(
Maps.filterKeys(target.extensions(), key -> !ignore.contains(key))
);
} else {
source.putAll(
target.extensions()
);
}
}
}

View File

@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.hswebframework.ezorm.core.Extensible;
import org.hswebframework.utils.time.DateFormatter;
import org.hswebframework.web.dict.EnumDict;
import org.hswebframework.web.proxy.Proxy;
@@ -179,6 +180,11 @@ public final class FastBeanCopier {
if (tartName.startsWith("package ")) {
tartName = tartName.substring("package ".length());
}
boolean targetIsExtensible = Extensible.class.isAssignableFrom(target);
boolean sourceIsExtensible = Extensible.class.isAssignableFrom(source);
boolean targetIsMap = Map.class.isAssignableFrom(target);
boolean sourceIsMap = Map.class.isAssignableFrom(source);
String method = "public void copy(Object s, Object t, java.util.Set ignore, " +
"org.hswebframework.web.bean.Converter converter){\n" +
"try{\n\t" +
@@ -194,7 +200,14 @@ public final class FastBeanCopier {
Proxy<Copier> proxy = Proxy
.create(Copier.class, new Class[]{source, target})
.addMethod(method);
return proxy.newInstance();
Copier copier = proxy.newInstance();
if (targetIsExtensible && sourceIsMap) {
copier = new ExtensibleToMapCopier(copier);
}
if (sourceIsMap && targetIsExtensible) {
copier = new MapToExtensibleCopier(copier);
}
return copier;
} catch (Exception e) {
log.error("创建bean copy 代理对象失败:\n{}", method, e);
throw new UnsupportedOperationException(e.getMessage(), e);
@@ -203,8 +216,10 @@ public final class FastBeanCopier {
private static Map<String, ClassProperty> createProperty(Class<?> type) {
List<String> fieldNames = Arrays.stream(type.getDeclaredFields())
.map(Field::getName).collect(Collectors.toList());
List<String> fieldNames = Arrays
.stream(type.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
return Stream.of(propertyUtils.getPropertyDescriptors(type))
.filter(property -> !property
@@ -218,8 +233,11 @@ public final class FastBeanCopier {
}
private static Map<String, ClassProperty> createMapProperty(Map<String, ClassProperty> template) {
return template.values().stream().map(classProperty -> new MapClassProperty(classProperty.name))
.collect(Collectors.toMap(ClassProperty::getName, Function.identity(), (k, k2) -> k, LinkedHashMap::new));
return template
.values()
.stream()
.map(classProperty -> new MapClassProperty(classProperty.name))
.collect(Collectors.toMap(ClassProperty::getName, Function.identity(), (k, k2) -> k, LinkedHashMap::new));
}
private static String createCopierCode(Class<?> source, Class<?> target) {
@@ -227,19 +245,20 @@ public final class FastBeanCopier {
Map<String, ClassProperty> targetProperties = null;
boolean targetIsExtensible = Extensible.class.isAssignableFrom(target);
boolean sourceIsExtensible = Extensible.class.isAssignableFrom(source);
boolean targetIsMap = Map.class.isAssignableFrom(target);
boolean sourceIsMap = Map.class.isAssignableFrom(source);
//源类型为Map
if (Map.class.isAssignableFrom(source)) {
if (!Map.class.isAssignableFrom(target)) {
if (sourceIsMap) {
if (!targetIsMap) {
targetProperties = createProperty(target);
sourceProperties = createMapProperty(targetProperties);
}
} else if (Map.class.isAssignableFrom(target)) {
if (!Map.class.isAssignableFrom(source)) {
sourceProperties = createProperty(source);
targetProperties = createMapProperty(sourceProperties);
}
} else if (targetIsMap) {
sourceProperties = createProperty(source);
targetProperties = createMapProperty(sourceProperties);
} else {
targetProperties = createProperty(target);
sourceProperties = createProperty(source);
@@ -252,6 +271,21 @@ public final class FastBeanCopier {
for (ClassProperty sourceProperty : sourceProperties.values()) {
ClassProperty targetProperty = targetProperties.get(sourceProperty.getName());
if (targetProperty == null) {
//复制到拓展对象
if (targetIsExtensible && !sourceIsExtensible) {
code.append("if(!ignore.contains(\"").append(sourceProperty.getName()).append("\")){\n\t");
if (!sourceProperty.isPrimitive()) {
code.append("if($$__source.").append(sourceProperty.getReadMethod()).append("!=null){\n");
}
code.append("\t\t((org.hswebframework.ezorm.core.Extensible)$$__target).setExtension(")
.append("\"").append(sourceProperty.name).append("\",")
.append("$$__source.").append(sourceProperty.getReadMethod())
.append(");");
if (!sourceProperty.isPrimitive()) {
code.append("\n\t}");
}
code.append("\n}\n");
}
continue;
}
code.append("if(!ignore.contains(\"").append(sourceProperty.getName()).append("\")){\n\t");

View File

@@ -0,0 +1,22 @@
package org.hswebframework.web.bean;
import lombok.AllArgsConstructor;
import org.hswebframework.ezorm.core.Extensible;
import java.util.Map;
import java.util.Set;
@AllArgsConstructor
class MapToExtensibleCopier implements Copier {
private final Copier copier;
@Override
public void copy(Object source, Object target, Set<String> ignore, Converter converter) {
copier.copy(source, target, ignore, converter);
ExtensibleUtils.copyFromMap((Map<String, Object>) source, ignore, (Extensible) target);
}
}

View File

@@ -4,6 +4,8 @@ import com.google.common.collect.ImmutableMap;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.hswebframework.ezorm.core.DefaultExtensible;
import org.hswebframework.ezorm.core.Extensible;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.util.ClassUtils;
@@ -23,6 +25,54 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class FastBeanCopierTest {
@Test
public void testExtensible() {
Source source = new Source();
source.setName("test");
source.setAge(123);
source.setColor(Color.RED);
ExtensibleEntity e = FastBeanCopier.copy(source, new ExtensibleEntity());
Assert.assertEquals(source.getName(), e.getName());
Assert.assertEquals(source.getAge(), e.getExtension("age"));
Assert.assertEquals(source.getColor(), e.getExtension("color"));
Map<String, Object> map = FastBeanCopier.copy(e, new HashMap<>());
System.out.println(map);
ExtensibleEntity t = FastBeanCopier.copy(map, new ExtensibleEntity());
Assert.assertEquals(e.getName(), t.getName());
System.out.println(e.extensions());
System.out.println(t.extensions());
Assert.assertEquals(e.extensions(), t.extensions());
}
@Test
public void testMapToExtensible() {
Source source = new Source();
source.setName("test");
source.setAge(123);
source.setColor(Color.RED);
Map<String, Object> map = FastBeanCopier.copy(source, new HashMap<>());
ExtensibleEntity e = FastBeanCopier.copy(map, new ExtensibleEntity());
Assert.assertEquals(source.getName(), e.getName());
Assert.assertEquals(source.getAge(), e.getExtension("age"));
Assert.assertEquals(source.getColor(), e.getExtension("color"));
}
@Getter
@Setter
public static class ExtensibleEntity extends DefaultExtensible {
private String name;
private boolean boy2;
}
@Test
public void test() throws InvocationTargetException, IllegalAccessException {
Source source = new Source();
@@ -68,7 +118,7 @@ public class FastBeanCopierTest {
@Test
public void testMapList() {
Map<String, Object> data = new HashMap<>();
data.put("templates", new HashMap() {
data.put("templates", new HashMap() {
{
put("0", Collections.singletonMap("name", "test"));
put("1", Collections.singletonMap("name", "test"));
@@ -80,7 +130,7 @@ public class FastBeanCopierTest {
Assert.assertNotNull(config);
Assert.assertNotNull(config.templates);
System.out.println(config.templates);
Assert.assertEquals(2,config.templates.size());
Assert.assertEquals(2, config.templates.size());
}
@@ -98,7 +148,7 @@ public class FastBeanCopierTest {
@Override
public String toString() {
return "name:"+name;
return "name:" + name;
}
}
@@ -132,8 +182,8 @@ public class FastBeanCopierTest {
System.out.println(clazz);
URLClassLoader loader = new URLClassLoader(new URL[]{
clazz
}, ClassUtils.getDefaultClassLoader()){
clazz
}, ClassUtils.getDefaultClassLoader()) {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
try {
@@ -174,13 +224,13 @@ public class FastBeanCopierTest {
Assert.assertNotSame(sourceClass, Source.class);
Object source = sourceClass.newInstance();
FastBeanCopier.copy(Collections.singletonMap("name","测试"),source);
FastBeanCopier.copy(Collections.singletonMap("name", "测试"), source);
Map<String,Object> map = FastBeanCopier.copy(source,new HashMap<>());
Map<String, Object> map = FastBeanCopier.copy(source, new HashMap<>());
System.out.println(map);
loader.close();
map = FastBeanCopier.copy(source,new HashMap<>());
map = FastBeanCopier.copy(source, new HashMap<>());
System.out.println(map);
@@ -193,17 +243,17 @@ public class FastBeanCopierTest {
ProxyTest test = (ProxyTest) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(),
new Class[]{ProxyTest.class}, (proxy, method, args) -> {
if (method.getName().equals("getName")) {
return "test";
}
if (method.getName().equals("setName")) {
reference.set(args[0]);
return null;
}
if (method.getName().equals("getName")) {
return "test";
}
if (method.getName().equals("setName")) {
reference.set(args[0]);
return null;
});
}
return null;
});
Target source = new Target();