diff --git a/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/Extensible.java b/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/Extensible.java new file mode 100644 index 00000000..5d40c087 --- /dev/null +++ b/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/Extensible.java @@ -0,0 +1,43 @@ +package org.hswebframework.ezorm.core; + +import com.fasterxml.jackson.annotation.JsonAnySetter; + +import java.util.Map; + +/** + * 可扩展的对象,用于动态拓展实体字段属性 + * + * @author zhouhao + * @since 4.1.3 + */ +public interface Extensible { + + /** + * 获取所有扩展属性 + * + * @return 扩展属性 + */ + @JsonAnySetter + Map extensions(); + + /** + * 获取扩展属性 + * + * @param property 属性名 + * @return 属性值 + */ + default Object getExtension(String property) { + Map ext = extensions(); + return ext == null ? null : ext.get(property); + } + + /** + * 设置扩展属性 + * + * @param property 属性名 + * @param value 属性值 + */ + @JsonAnySetter + void setExtension(String property, Object value); + +} diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/defaults/DefaultRepository.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/defaults/DefaultRepository.java index c1d85ebf..281a583d 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/defaults/DefaultRepository.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/defaults/DefaultRepository.java @@ -2,15 +2,13 @@ import lombok.Getter; import lombok.Setter; +import org.hswebframework.ezorm.core.Extensible; import org.hswebframework.ezorm.core.GlobalConfig; -import org.hswebframework.ezorm.core.ObjectPropertyOperator; -import org.hswebframework.ezorm.core.RuntimeDefaultValue; -import org.hswebframework.ezorm.rdb.events.ContextKey; import org.hswebframework.ezorm.rdb.events.ContextKeyValue; import org.hswebframework.ezorm.rdb.events.ContextKeys; -import org.hswebframework.ezorm.rdb.executor.NullValue; import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrapper; import org.hswebframework.ezorm.rdb.mapping.EntityColumnMapping; +import org.hswebframework.ezorm.rdb.mapping.EntityPropertyDescriptor; import org.hswebframework.ezorm.rdb.mapping.LazyEntityColumnMapping; import org.hswebframework.ezorm.rdb.mapping.MappingFeatureType; import org.hswebframework.ezorm.rdb.mapping.events.EventResultOperator; @@ -22,18 +20,12 @@ import org.hswebframework.ezorm.rdb.operator.builder.fragments.NativeSql; import org.hswebframework.ezorm.rdb.operator.dml.insert.InsertOperator; import org.hswebframework.ezorm.rdb.operator.dml.insert.InsertResultOperator; -import org.hswebframework.ezorm.rdb.operator.dml.upsert.SaveOrUpdateOperator; import org.hswebframework.ezorm.rdb.operator.dml.upsert.SaveResultOperator; import org.hswebframework.ezorm.rdb.operator.dml.upsert.UpsertOperator; -import reactor.core.publisher.Mono; +import org.hswebframework.ezorm.rdb.utils.PropertyUtils; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.util.*; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Stream; @@ -138,24 +130,27 @@ protected Collection tryMergeDuplicate(Collection data) { } protected E merge(E older, E newer) { - ObjectPropertyOperator opt = GlobalConfig.getPropertyOperator(); for (String property : getProperties()) { - Object newerVal = opt.getProperty(newer, property).orElse(null); + + Object newerVal = getProperty(newer, property); if (newerVal != null) { continue; } - opt.getProperty(older, property) - .ifPresent(olderValue -> opt.setProperty(newer, property, olderValue)); + newerVal = getProperty(older, property); + if (newerVal != null) { + setProperty(newer, property, newerVal); + } } return newer; } - private Object getProperty(E data, String property) { - return GlobalConfig - .getPropertyOperator() - .getProperty(data, property) - .orElse(null); + void setProperty(E entity, String property, Object value) { + PropertyUtils.setProperty(entity, property, value, mapping); + } + + Object getProperty(E entity, String property) { + return PropertyUtils.getProperty(entity, property, mapping).orElse(null); } protected SaveResultOperator doSave(Collection data) { @@ -213,7 +208,7 @@ protected InsertResultOperator doInsert(E data) { } private Object getInsertColumnValue(E data, String property, BiConsumer whenDefaultValue) { - Object value = GlobalConfig.getPropertyOperator().getProperty(data, property).orElse(null); + Object value = getProperty(data, property); if (value == null) { value = mapping.getColumnByProperty(property) .flatMap(RDBColumnMetadata::generateDefaultValue) @@ -222,7 +217,7 @@ private Object getInsertColumnValue(E data, String property, BiConsumer includes.isEmpty() || includes.contains(e.getKey()) || includes.contains(e.getValue())) .filter(e -> !excludes.contains(e.getKey()) && !excludes.contains(e.getValue())) - .forEach(e -> GlobalConfig - .getPropertyOperator() - .getProperty(instance, e.getValue()) + .forEach(e -> PropertyUtils + .getProperty(instance, e.getValue(), mapping) .ifPresent(val -> this.set(e.getKey(), val))); } + @Override public ME includes(String... properties) { includes.addAll(Arrays.asList(properties)); diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/EntityResultWrapper.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/EntityResultWrapper.java index 261ebfa2..d9ae2920 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/EntityResultWrapper.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/EntityResultWrapper.java @@ -9,6 +9,7 @@ import org.hswebframework.ezorm.rdb.mapping.EntityColumnMapping; import org.hswebframework.ezorm.rdb.mapping.MappingFeatureType; import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata; +import org.hswebframework.ezorm.rdb.utils.PropertyUtils; import java.util.Optional; import java.util.function.Supplier; @@ -46,7 +47,7 @@ public void wrapColumn(ColumnWrapperContext context) { .map(columnMetadata -> columnMetadata.decode(context.getResult())) .orElseGet(context::getResult); if (value != null) { - GlobalConfig.getPropertyOperator().setProperty(context.getRowInstance(), property, value); + PropertyUtils.setProperty(context.getRowInstance(), property, value,mapping); } } diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/NestedEntityResultWrapper.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/NestedEntityResultWrapper.java index 69a45bda..0fb6984c 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/NestedEntityResultWrapper.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/mapping/wrapper/NestedEntityResultWrapper.java @@ -1,9 +1,12 @@ package org.hswebframework.ezorm.rdb.mapping.wrapper; +import org.hswebframework.ezorm.core.Extensible; import org.hswebframework.ezorm.core.GlobalConfig; import org.hswebframework.ezorm.rdb.executor.wrapper.ColumnWrapperContext; import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrapper; import org.hswebframework.ezorm.rdb.mapping.EntityColumnMapping; +import org.hswebframework.ezorm.rdb.mapping.EntityPropertyDescriptor; +import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; import org.hswebframework.ezorm.rdb.metadata.key.ForeignKeyMetadata; public class NestedEntityResultWrapper implements ResultWrapper { @@ -37,18 +40,31 @@ public void wrapColumn(ColumnWrapperContext context) { } ForeignKeyMetadata metadata = mapping.getTable().getForeignKey(table).orElse(null); if (null != metadata) { - Object value = metadata - .getTarget() - .getColumn(column) - .map(m -> m.decode(context.getResult())) - .orElseGet(context::getResult); - GlobalConfig.getPropertyOperator().setProperty(nestInstance, column, value); + RDBColumnMetadata col = metadata.getTarget().getColumn(column).orElse(null); + + Object val = col == null ? context.getResult() : col.decode(context.getResult()); + + setProperty(col, context.getRowInstance(), label, val); } } else { - GlobalConfig.getPropertyOperator().setProperty(context.getRowInstance(), label, context.getResult()); + setProperty(mapping.getColumnByProperty(label).orElse(null), + context.getRowInstance(), + label, + context.getResult()); } } + protected void setProperty(RDBColumnMetadata col, E instance, String label, Object val) { + if (instance instanceof Extensible && (col == null || !col + .getFeature(EntityPropertyDescriptor.ID) + .isPresent())) { + ((Extensible) instance).setExtension(label, val); + } else { + GlobalConfig.getPropertyOperator().setProperty(instance, label, val); + } + + } + @Override public boolean completedWrapRow(E result) { return true; diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/dml/query/BuildParameterQueryOperator.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/dml/query/BuildParameterQueryOperator.java index 897a5570..5291e417 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/dml/query/BuildParameterQueryOperator.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/dml/query/BuildParameterQueryOperator.java @@ -35,16 +35,18 @@ public BuildParameterQueryOperator(String from) { @Override public QueryOperator select(Collection columns) { - - columns.stream() - .map(SelectColumn::of) - .forEach(parameter.getSelect()::add); + for (String column : columns) { + parameter.getSelect().add(SelectColumn.of(column)); + } return this; } @Override public QueryOperator select(String... columns) { - return select(Arrays.asList(columns)); + for (String column : columns) { + parameter.getSelect().add(SelectColumn.of(column)); + } + return this; } @Override diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/utils/PropertyUtils.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/utils/PropertyUtils.java new file mode 100644 index 00000000..c8bbd749 --- /dev/null +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/utils/PropertyUtils.java @@ -0,0 +1,35 @@ +package org.hswebframework.ezorm.rdb.utils; + +import org.hswebframework.ezorm.core.Extensible; +import org.hswebframework.ezorm.core.GlobalConfig; +import org.hswebframework.ezorm.core.ObjectPropertyOperator; +import org.hswebframework.ezorm.rdb.mapping.EntityColumnMapping; +import org.hswebframework.ezorm.rdb.mapping.EntityPropertyDescriptor; + +import java.util.Optional; + +public class PropertyUtils { + + public static Optional getProperty(Object entity, String property, EntityColumnMapping mapping) { + ObjectPropertyOperator opt = GlobalConfig.getPropertyOperator(); + if (entity instanceof Extensible && isExtensibleColumn(property, mapping)) { + return Optional.ofNullable(((Extensible) entity).getExtension(property)); + } + return opt.getProperty(entity, property); + } + + public static boolean isExtensibleColumn(String property, EntityColumnMapping mapping) { + return mapping + .getColumnByProperty(property) + .map(c -> !c.getFeature(EntityPropertyDescriptor.ID).isPresent()) + .orElse(true); + } + + public static void setProperty(Object entity, String property, Object value, EntityColumnMapping mapping) { + if (entity instanceof Extensible && isExtensibleColumn(property, mapping)) { + ((Extensible) entity).setExtension(property, value); + } else { + GlobalConfig.getPropertyOperator().setProperty(entity, property, value); + } + } +} diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/Containers.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/Containers.java index 8c0e3544..53992a2c 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/Containers.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/Containers.java @@ -4,6 +4,8 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.DockerImageName; +import java.time.Duration; + public class Containers { public static GenericContainer newMysql(String version) { @@ -32,7 +34,7 @@ public static GenericContainer newOracle() { return new GenericContainer<>(DockerImageName.parse("iatebes/oracle_11g")) .withEnv("TZ", "Asia/Shanghai") .withExposedPorts(1521) - .waitingFor(Wait.forLogMessage(".*opened.*", 1)); + .waitingFor(Wait.forLogMessage(".*opened.*", 1).withStartupTimeout(Duration.ofMinutes(2))); } public static GenericContainer newMSSQL() { @@ -42,7 +44,7 @@ public static GenericContainer newMSSQL() { .withEnv("ACCEPT_EULA","y") .withEnv("MSSQL_PID","Enterprise") .withExposedPorts(1433) - .waitingFor(Wait.forLogMessage(".*Service Broker manager has started.*",1)); + .waitingFor(Wait.forLogMessage(".*Service Broker manager has started.*",1).withStartupTimeout(Duration.ofMinutes(2))); } } diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicReactiveTests.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicReactiveTests.java index d9db24eb..35c7cd5a 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicReactiveTests.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicReactiveTests.java @@ -132,9 +132,15 @@ public void init() { operator.ddl() .createOrAlter(table) + .addColumn("ext_name") + .varchar(32) + .commit() .commit() .reactive() .block(); + table.getFeatureNow(MappingFeatureType.columnPropertyMapping.createFeatureId(BasicTestEntity.class)) + .reload(); + EntityResultWrapper wrapper = new EntityResultWrapper<>(BasicTestEntity::new); wrapper.setMapping(table .getFeature(MappingFeatureType.columnPropertyMapping.createFeatureId(BasicTestEntity.class)) @@ -144,6 +150,7 @@ public void init() { repository = new DefaultReactiveRepository<>(operator, table, BasicTestEntity.class, wrapper); addressRepository = operator.dml().createReactiveRepository("test_address"); + } @Test @@ -268,6 +275,8 @@ public void testInsertMerge() { .stateEnum(StateEnum.enabled) .build(); + second.setExtension("ext_name", "test"); + repository .insert(Flux.just(first, second)) .as(StepVerifier::create) @@ -277,8 +286,9 @@ public void testInsertMerge() { repository .createQuery() .where(BasicTestEntity::getId, first.getId()) - .select("id", "name") + .select("id", "name","ext_name") .fetch() + .doOnNext(System.out::println) .map(BasicTestEntity::getName) .as(StepVerifier::create) .expectNext(second.getName()) @@ -578,7 +588,7 @@ public void testEnums() { } @Test - public void testUpdateByNative(){ + public void testUpdateByNative() { BasicTestEntity entity = BasicTestEntity .builder() .id("testUpdateByNative") @@ -593,13 +603,13 @@ public void testUpdateByNative(){ .verifyComplete(); repository.createUpdate() - .set(BasicTestEntity::getState, NativeSql.of("state + 1")) - .where(entity::getId) - .lte(BasicTestEntity::getState,NativeSql.of("state + 1")) - .execute() - .as(StepVerifier::create) - .expectNext(1) - .verifyComplete(); + .set(BasicTestEntity::getState, NativeSql.of("state + 1")) + .where(entity::getId) + .lte(BasicTestEntity::getState, NativeSql.of("state + 1")) + .execute() + .as(StepVerifier::create) + .expectNext(1) + .verifyComplete(); } diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicTestEntity.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicTestEntity.java index 95c9c015..acb6afbe 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicTestEntity.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/BasicTestEntity.java @@ -2,6 +2,7 @@ import lombok.*; import org.hswebframework.ezorm.core.DefaultValueGenerator; +import org.hswebframework.ezorm.core.Extensible; import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType; import org.hswebframework.ezorm.rdb.mapping.annotation.DefaultValue; import org.hswebframework.ezorm.rdb.mapping.annotation.EnumCodec; @@ -11,16 +12,16 @@ import java.io.Serializable; import java.math.BigDecimal; import java.sql.JDBCType; -import java.util.Date; -import java.util.List; +import java.util.*; @Table(name = "entity_test_table", indexes = @Index(name = "test_index", columnList = "name,state desc")) @Data @Builder @AllArgsConstructor @NoArgsConstructor -@EqualsAndHashCode(exclude = {"createTime","address"}) -public class BasicTestEntity implements Serializable { +@EqualsAndHashCode(exclude = {"createTime", "address"}) +@ToString +public class BasicTestEntity implements Serializable, Extensible { @Column(length = 32) @Id @@ -64,7 +65,7 @@ public class BasicTestEntity implements Serializable { private StateEnum stateEnum; @Column - @ColumnType(javaType = Long.class,jdbcType = JDBCType.BIGINT) + @ColumnType(javaType = Long.class, jdbcType = JDBCType.BIGINT) @EnumCodec(toMask = true) @DefaultValue("0") private StateEnum[] stateEnums; @@ -81,4 +82,23 @@ public class BasicTestEntity implements Serializable { @JoinColumn(name = "address_id") private Address address; + + @Builder.Default + private transient Map extensions = new HashMap<>(); + + @Override + public Map extensions() { + return extensions; + } + + @Override + public Object getExtension(String property) { + return extensions.get(property); + } + + @Override + public void setExtension(String property, Object value) { + extensions.put(property, value); + } + } diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/mssql/MSSQLConnectionProvider.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/mssql/MSSQLConnectionProvider.java index 4f60460d..bbb9c40c 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/mssql/MSSQLConnectionProvider.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/mssql/MSSQLConnectionProvider.java @@ -19,10 +19,14 @@ public class MSSQLConnectionProvider implements ConnectionProvider { } catch (ClassNotFoundException e) { e.printStackTrace(); } - GenericContainer container = Containers.newMSSQL(); + try { + GenericContainer container = Containers.newMSSQL(); - container.start(); - port = container.getMappedPort(1433); + container.start(); + port = container.getMappedPort(1433); + }catch (Throwable e){ + e.printStackTrace(); + } } @SneakyThrows