diff --git a/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/ValueCodec.java b/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/ValueCodec.java index 0ff03dc8..386da5dd 100644 --- a/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/ValueCodec.java +++ b/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/ValueCodec.java @@ -1,6 +1,11 @@ package org.hswebframework.ezorm.core; public interface ValueCodec extends Encoder, Decoder { + + default E encodeNull(){ + return null; + } + E encode(Object value); D decode(Object data); diff --git a/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/meta/AbstractColumnMetadata.java b/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/meta/AbstractColumnMetadata.java index d9543d28..2c2b48d5 100644 --- a/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/meta/AbstractColumnMetadata.java +++ b/hsweb-easy-orm-core/src/main/java/org/hswebframework/ezorm/core/meta/AbstractColumnMetadata.java @@ -57,6 +57,9 @@ public Object decode(Object data) { public Object encode(Object data) { if (data == null) { + if (valueCodec != null) { + return valueCodec.encodeNull(); + } return null; } if (valueCodec != null) { diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/codec/DateTimeCodec.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/codec/DateTimeCodec.java index 5f6d4e1e..56cbec04 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/codec/DateTimeCodec.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/codec/DateTimeCodec.java @@ -118,6 +118,8 @@ public Object toDate(Object data) { } else if (data instanceof ZonedDateTime) { ZonedDateTime dateTime = ((ZonedDateTime) data); data = Date.from(dateTime.toInstant()); + }else if(data instanceof OffsetDateTime){ + data = Date.from(((OffsetDateTime) data).toInstant()); } else if (data instanceof String) { String stringData = ((String) data); if ((stringData).contains(",")) { diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBColumnMetadata.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBColumnMetadata.java index 5cc29876..4755e227 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBColumnMetadata.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBColumnMetadata.java @@ -219,7 +219,13 @@ public RDBColumnMetadata clone() { @Override public Object encode(Object data) { - if (data instanceof NullValue) { + if (data == null || data instanceof NullValue) { + if (valueCodec != null) { + Object newVal = valueCodec.encodeNull(); + if (newVal != null) { + return newVal; + } + } return data; } return super.encode(data); diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBIndexMetadata.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBIndexMetadata.java index 58e073ee..5f7a05d7 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBIndexMetadata.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBIndexMetadata.java @@ -42,10 +42,10 @@ public RDBIndexMetadata(String name) { @Override public String toString() { StringBuilder builder = new StringBuilder(name) - .append(" ") - .append(unique ? "unique index" : "index") - .append(" on ") - .append(tableName); + .append(" ") + .append(unique ? "unique index" : "index") + .append(" on ") + .append(tableName); builder.append("("); int index = 0; for (IndexColumn column : columns) { @@ -74,12 +74,11 @@ public ObjectType getObjectType() { @SneakyThrows public RDBIndexMetadata clone() { RDBIndexMetadata metadata = (RDBIndexMetadata) super.clone(); + metadata.columns = new CopyOnWriteArrayList<>(); - metadata.columns.clear(); - - columns.stream() - .map(IndexColumn::clone) - .forEach(metadata.columns::add); + for (IndexColumn column : this.columns) { + metadata.columns.add(column.clone()); + } return metadata; } @@ -94,17 +93,17 @@ public boolean isChanged(RDBTableMetadata metadata, RDBIndexMetadata old) { } Map nameMapping = getColumns() - .stream() - .collect(Collectors.toMap(c -> metadata - .getColumn(c.column) - .map(RDBColumnMetadata::getName) - .orElseGet(RDBIndexMetadata.class::getName), Function.identity())); + .stream() + .collect(Collectors.toMap(c -> metadata + .getColumn(c.column) + .map(RDBColumnMetadata::getName) + .orElseGet(RDBIndexMetadata.class::getName), Function.identity())); for (IndexColumn oldColumn : old.getColumns()) { String columnName = metadata - .getColumn(oldColumn.column) - .map(RDBColumnMetadata::getName) - .orElse(null); + .getColumn(oldColumn.column) + .map(RDBColumnMetadata::getName) + .orElse(null); if (columnName == null || !nameMapping.containsKey(columnName)) { diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/insert/BatchInsertSqlBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/insert/BatchInsertSqlBuilder.java index c20bb0dc..611221f0 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/insert/BatchInsertSqlBuilder.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/insert/BatchInsertSqlBuilder.java @@ -48,8 +48,8 @@ public SqlRequest build(InsertOperatorParameter parameter) { fragments.add(SqlFragments.LEFT_BRACKET); - LinkedHashMap indexMapping = Maps.newLinkedHashMapWithExpectedSize(columns.size()); - LinkedHashMap functionValues = Maps.newLinkedHashMapWithExpectedSize(columns.size()); + LinkedHashMap indexMapping = Maps.newLinkedHashMapWithExpectedSize(columns.size()); + LinkedHashMap functionValues = Maps.newLinkedHashMapWithExpectedSize(columns.size()); int index = 0; int primaryIndex = -1; @@ -140,8 +140,16 @@ public SqlRequest build(InsertOperatorParameter parameter) { if (value == null) { value = NullValue.of(column.getType()); } - fragments.add(SqlFragments.QUESTION_MARK) - .addParameter(column.encode(value)); + value = column.encode(value); + if (value instanceof NativeSql) { + fragments + .addSql(((NativeSql) value).getSql()) + .addParameter(((NativeSql) value).getParameters()); + + } else { + fragments.add(SqlFragments.QUESTION_MARK) + .addParameter(value); + } } } } diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/update/DefaultUpdateSqlBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/update/DefaultUpdateSqlBuilder.java index 77bb5191..a688d7b0 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/update/DefaultUpdateSqlBuilder.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/update/DefaultUpdateSqlBuilder.java @@ -90,9 +90,18 @@ else if (value instanceof NativeSql) { } // = ? else { - columnFragments = SimpleSqlFragments - .of(Arrays.asList(columnMetadata.getQuoteName(), "= ?"), - Collections.singletonList(columnMetadata.encode(value))); + value = columnMetadata.encode(value); + if (value instanceof NativeSql) { + columnFragments = SimpleSqlFragments + .of( + Arrays.asList(columnMetadata.getQuoteName(), "=", ((NativeSql) column.getValue()).getSql()), + Arrays.asList(((NativeSql) column.getValue()).getParameters()) + ); + } else { + columnFragments = SimpleSqlFragments + .of(Arrays.asList(columnMetadata.getQuoteName(), "= ?"), + Collections.singletonList(columnMetadata.encode(value))); + } } } diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBatchUpsertOperator.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBatchUpsertOperator.java index 6935d6b4..b94c9b89 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBatchUpsertOperator.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBatchUpsertOperator.java @@ -8,6 +8,7 @@ import org.hswebframework.ezorm.rdb.executor.reactive.ReactiveSqlExecutor; import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult; import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBIndexMetadata; import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata; import org.hswebframework.ezorm.rdb.operator.builder.fragments.*; import org.hswebframework.ezorm.rdb.operator.builder.fragments.insert.BatchInsertSqlBuilder; @@ -19,8 +20,10 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.Supplier; +import java.util.stream.Collectors; @SuppressWarnings("all") public class PostgresqlBatchUpsertOperator implements SaveOrUpdateOperator { @@ -29,36 +32,60 @@ public class PostgresqlBatchUpsertOperator implements SaveOrUpdateOperator { private PostgresqlUpsertBatchInsertSqlBuilder builder; - private RDBColumnMetadata idColumn; + private SqlFragments prefix; private SaveOrUpdateOperator fallback; public PostgresqlBatchUpsertOperator(RDBTableMetadata table) { this.table = table; - this.idColumn = table.getColumns() - .stream().filter(RDBColumnMetadata::isPrimaryKey) - .findFirst() - .orElse(null); this.fallback = new DefaultSaveOrUpdateOperator(table); this.builder = new PostgresqlUpsertBatchInsertSqlBuilder(table); } @Override public SaveResultOperator execute(UpsertOperatorParameter parameter) { - if (idColumn == null) { - this.idColumn = table + if (getOrCreateOnConflict().isEmpty()) { + return fallback.execute(parameter); + } + return new PostgresqlSaveResultOperator(() -> builder.build(new PostgresqlUpsertOperatorParameter(parameter))); + } + + SqlFragments getOrCreateOnConflict() { + if (prefix == null) { + prefix = createOnConflict(); + } + return prefix; + } + + SqlFragments createOnConflict() { + RDBColumnMetadata idColumn = table + .getColumns() + .stream() + .filter(RDBColumnMetadata::isPrimaryKey) + .findFirst() + .orElse(null); + if (idColumn != null) { + return SqlFragments.of("on conflict (", idColumn.getName(), ") do "); + } + RDBIndexMetadata indexMetadata = table + .getIndexes() + .stream() + .filter(index -> index.isUnique()) + .findFirst() + .orElse(null); + + if (indexMetadata != null) { + String columns = indexMetadata .getColumns() .stream() - .filter(RDBColumnMetadata::isPrimaryKey) - .findFirst() - .orElse(null); + .map(c -> table.getColumn(c.getColumn()).orElse(null)) + .filter(Objects::nonNull) + .map(RDBColumnMetadata::getQuoteName) + .collect(Collectors.joining(",")); - if (this.idColumn == null) { - return fallback.execute(parameter); - } + return SqlFragments.of("on conflict( ", columns, ") do "); } - - return new PostgresqlSaveResultOperator(() -> builder.build(new PostgresqlUpsertOperatorParameter(parameter))); + return EmptySqlFragments.INSTANCE; } class PostgresqlUpsertOperatorParameter extends InsertOperatorParameter { @@ -104,7 +131,6 @@ public Mono reactive() { private class PostgresqlUpsertBatchInsertSqlBuilder extends BatchInsertSqlBuilder { - SqlFragments PREFIX = null; public PostgresqlUpsertBatchInsertSqlBuilder(RDBTableMetadata table) { super(table); @@ -117,10 +143,7 @@ protected int computeSqlSize(int columnSize, int valueSize) { @Override protected AppendableSqlFragments afterBuild(Set columns, InsertOperatorParameter parameter, AppendableSqlFragments sql) { - if (PREFIX == null) { - PREFIX = SqlFragments.of("on conflict (", idColumn.getName(), ") do "); - } - sql.add(PREFIX); + sql.add(createOnConflict()); if (((PostgresqlUpsertOperatorParameter) parameter).doNoThingOnConflict) { sql.addSql("nothing"); diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlDialect.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlDialect.java index 5d25c47b..9c650473 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlDialect.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlDialect.java @@ -7,6 +7,8 @@ import java.math.BigDecimal; import java.sql.JDBCType; +import java.sql.Timestamp; +import java.time.ZonedDateTime; /** * @author zhouhao @@ -20,6 +22,7 @@ public PostgresqlDialect() { addDataTypeBuilder(JDBCType.CHAR, (meta) -> StringUtils.concat("char(", meta.getLength(255), ")")); addDataTypeBuilder(JDBCType.VARCHAR, (meta) -> StringUtils.concat("varchar(", meta.getLength(255), ")")); addDataTypeBuilder(JDBCType.TIMESTAMP, (meta) -> "timestamp"); + addDataTypeBuilder(JDBCType.TIMESTAMP_WITH_TIMEZONE, (meta) -> "timestamptz"); addDataTypeBuilder(JDBCType.TIME, (meta) -> "time"); addDataTypeBuilder(JDBCType.DATE, (meta) -> "date"); addDataTypeBuilder(JDBCType.CLOB, (meta) -> "text"); @@ -36,6 +39,7 @@ public PostgresqlDialect() { addDataTypeBuilder(JDBCType.BIGINT, (meta) -> "bigint"); addDataTypeBuilder(JDBCType.OTHER, (meta) -> "other"); + addDataTypeBuilder("json", meta -> "json"); addDataTypeBuilder("jsonb", meta -> "jsonb"); @@ -46,6 +50,10 @@ public PostgresqlDialect() { registerDataType("blob", DataType.builder(JdbcDataType.of(JDBCType.BLOB, String.class), (c) -> "bytea")); registerDataType("longnvarchar", DataType.builder(JdbcDataType.of(JDBCType.LONGNVARCHAR, String.class), c -> "text")); registerDataType("longvarchar", DataType.builder(JdbcDataType.of(JDBCType.LONGVARCHAR, String.class), c -> "text")); + registerDataType("timestamptz", DataType + .builder(JdbcDataType + .of(JDBCType.TIMESTAMP_WITH_TIMEZONE, ZonedDateTime.class), + c -> "timestamptz")); registerDataType("int8", JdbcDataType.of(JDBCType.BIGINT, Long.class)); registerDataType("int4", JdbcDataType.of(JDBCType.INTEGER, Integer.class)); diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlIndexMetadataParser.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlIndexMetadataParser.java index 67276b8b..2aeb6a41 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlIndexMetadataParser.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlIndexMetadataParser.java @@ -22,6 +22,7 @@ import reactor.util.function.Tuples; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; @AllArgsConstructor @Slf4j @@ -110,7 +111,7 @@ public Flux parseAllReactive() { } class PostgresqlIndexMetadataWrapper implements ResultWrapper, List> { - Map, RDBIndexMetadata> group = new HashMap<>(); + Map, RDBIndexMetadata> group = new ConcurrentHashMap<>(); @Override public Map newRowInstance() { @@ -127,16 +128,15 @@ public void wrapColumn(ColumnWrapperContext> context) { @Override public boolean completedWrapRow(Map result) { String name = (String) result.get("indexname"); - String tableName = ((String) result.get("tablename")).toLowerCase(); + String tableName = ((String) result.get("tablename")); RDBIndexMetadata index = group.computeIfAbsent(Tuples.of(name,tableName), __ -> new RDBIndexMetadata()); index.setName(name.toLowerCase()); index.setTableName(tableName); index.setPrimaryKey(Boolean.TRUE.equals(result.get("indisprimary"))); - index.setUnique(Boolean.FALSE.equals(result.get("indisunique"))); + index.setUnique(Boolean.TRUE.equals(result.get("indisunique"))); RDBIndexMetadata.IndexColumn indexColumn = new RDBIndexMetadata.IndexColumn(); - indexColumn.setColumn(((String) result.get("attname")).toLowerCase()); - // TODO: 2019-10-22 咋获取排序... + indexColumn.setColumn(((String) result.get("attname"))); indexColumn.setSort(RDBIndexMetadata.IndexSort.asc); indexColumn.setSortIndex(((Number) result.get("attnum")).intValue()); index.getColumns().add(indexColumn);