diff --git a/dbswitch-common/src/main/java/com/gitee/dbswitch/common/util/ObjectCastUtils.java b/dbswitch-common/src/main/java/com/gitee/dbswitch/common/util/ObjectCastUtils.java index 77d75e10..b36ad42c 100644 --- a/dbswitch-common/src/main/java/com/gitee/dbswitch/common/util/ObjectCastUtils.java +++ b/dbswitch-common/src/main/java/com/gitee/dbswitch/common/util/ObjectCastUtils.java @@ -933,6 +933,7 @@ public final class ObjectCastUtils { public static Object castByJdbcType(int jdbcType, Object value) { switch (jdbcType) { + case Types.BIT: case Types.TINYINT: return convert(value, ObjectCastUtils::castToByte); case Types.SMALLINT: @@ -950,7 +951,6 @@ public final class ObjectCastUtils { case Types.DOUBLE: return convert(value, ObjectCastUtils::castToDouble); case Types.BOOLEAN: - case Types.BIT: return convert(value, ObjectCastUtils::castToBoolean); case Types.TIME: return convert(value, ObjectCastUtils::castToLocalTime); diff --git a/dbswitch-core/src/main/java/com/gitee/dbswitch/calculate/DefaultChangeCalculatorService.java b/dbswitch-core/src/main/java/com/gitee/dbswitch/calculate/DefaultChangeCalculatorService.java index ae25292f..d7634611 100644 --- a/dbswitch-core/src/main/java/com/gitee/dbswitch/calculate/DefaultChangeCalculatorService.java +++ b/dbswitch-core/src/main/java/com/gitee/dbswitch/calculate/DefaultChangeCalculatorService.java @@ -22,6 +22,9 @@ import com.gitee.dbswitch.service.MetadataService; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -480,6 +483,24 @@ public final class DefaultChangeCalculatorService implements RecordRowChangeCalc java.lang.Number s2 = (java.lang.Number) o2; return Long.compare(s1.longValue(), s2.longValue()); } else if (JdbcTypesUtils.isDateTime(type)) { + if (o1 instanceof LocalTime) { + o1 = java.sql.Time.valueOf((LocalTime) o1); + } + if (o1 instanceof LocalDate) { + o1 = java.sql.Date.valueOf((LocalDate) o1); + } + if (o1 instanceof LocalDateTime) { + o1 = java.sql.Timestamp.valueOf((LocalDateTime) o1); + } + if (o2 instanceof LocalTime) { + o2 = java.sql.Time.valueOf((LocalTime) o2); + } + if (o2 instanceof LocalDate) { + o2 = java.sql.Date.valueOf((LocalDate) o2); + } + if (o2 instanceof LocalDateTime) { + o2 = java.sql.Timestamp.valueOf((LocalDateTime) o2); + } if (o1 instanceof java.sql.Time && o2 instanceof java.sql.Time) { java.sql.Time t1 = (java.sql.Time) o1; java.sql.Time t2 = (java.sql.Time) o2; diff --git a/dbswitch-core/src/main/java/com/gitee/dbswitch/provider/sync/DefaultTableDataSynchronizeProvider.java b/dbswitch-core/src/main/java/com/gitee/dbswitch/provider/sync/DefaultTableDataSynchronizeProvider.java index d94d4a74..f8812d5f 100644 --- a/dbswitch-core/src/main/java/com/gitee/dbswitch/provider/sync/DefaultTableDataSynchronizeProvider.java +++ b/dbswitch-core/src/main/java/com/gitee/dbswitch/provider/sync/DefaultTableDataSynchronizeProvider.java @@ -9,6 +9,7 @@ ///////////////////////////////////////////////////////////// package com.gitee.dbswitch.provider.sync; +import cn.hutool.json.JSONUtil; import com.gitee.dbswitch.common.type.ProductTypeEnum; import com.gitee.dbswitch.provider.AbstractCommonProvider; import com.gitee.dbswitch.provider.ProductFactoryProvider; @@ -105,15 +106,24 @@ public class DefaultTableDataSynchronizeProvider } try { - int[] affects = jdbcTemplate - .batchUpdate(this.insertStatementSql, records, this.insertArgsType); - int affectCount = 0; - for (int i : affects) { - affectCount += i; + try { + jdbcTemplate.batchUpdate(this.insertStatementSql, records, this.insertArgsType); + } catch (Exception e) { + if (e instanceof java.sql.BatchUpdateException) { + for (Object[] dataList : records) { + try { + jdbcTemplate.update(this.insertStatementSql, dataList, this.updateArgsType); + } catch (Exception ex) { + log.error("Failed to insert by SQL: {}, value: {}", this.insertStatementSql, + JSONUtil.toJsonStr(dataList)); + throw ex; + } + } + } } tx.commit(status); - return affectCount; + return records.size(); } catch (TransactionException e) { tx.rollback(status); throw e; @@ -150,13 +160,24 @@ public class DefaultTableDataSynchronizeProvider } try { - int[] affects = jdbcTemplate.batchUpdate(this.updateStatementSql, dataLists, this.updateArgsType); - int affectCount = 0; - for (int i : affects) { - affectCount += i; + try { + jdbcTemplate.batchUpdate(this.updateStatementSql, dataLists, this.updateArgsType); + } catch (Exception e) { + if (e instanceof java.sql.BatchUpdateException) { + for (Object[] dataList : dataLists) { + try { + jdbcTemplate.update(this.updateStatementSql, dataList, this.updateArgsType); + } catch (Exception ex) { + log.error("Failed to update by SQL: {}, value: {}", this.updateStatementSql, + JSONUtil.toJsonStr(dataList)); + throw ex; + } + } + } } + tx.commit(status); - return affectCount; + return dataLists.size(); } catch (TransactionException e) { tx.rollback(status); throw e; @@ -185,14 +206,9 @@ public class DefaultTableDataSynchronizeProvider } try { - int[] affects = jdbcTemplate.batchUpdate(this.deleteStatementSql, dataLists, this.deleteArgsType); - int affectCount = 0; - for (int i : affects) { - affectCount += i; - } - + jdbcTemplate.batchUpdate(this.deleteStatementSql, dataLists, this.deleteArgsType); tx.commit(status); - return affectCount; + return dataLists.size(); } catch (TransactionException e) { tx.rollback(status); throw e; diff --git a/dbswitch-product/dbswitch-product-oracle/src/main/java/com/gitee/dbswitch/product/oracle/OracleCastUtils.java b/dbswitch-product/dbswitch-product-oracle/src/main/java/com/gitee/dbswitch/product/oracle/OracleCastUtils.java index 00c76b0b..399a7b07 100644 --- a/dbswitch-product/dbswitch-product-oracle/src/main/java/com/gitee/dbswitch/product/oracle/OracleCastUtils.java +++ b/dbswitch-product/dbswitch-product-oracle/src/main/java/com/gitee/dbswitch/product/oracle/OracleCastUtils.java @@ -1,11 +1,21 @@ package com.gitee.dbswitch.product.oracle; +import com.gitee.dbswitch.common.util.JdbcTypesUtils; import com.gitee.dbswitch.common.util.ObjectCastUtils; import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.io.StringReader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Date; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; import java.sql.Types; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.List; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; @@ -18,7 +28,7 @@ public class OracleCastUtils { /** * 将java.sql.Array 类型转换为java.lang.String *

- * Oracle 没有数组类型,这里以文本类型进行存在 + * Oracle 没有数组类型,这里以文本类型进行存储 *

* Oracle的CLOB和BLOB类型写入请见: *

@@ -29,28 +39,137 @@ public class OracleCastUtils { return null; } - if (jdbcType == Types.BLOB) { - try { - final byte[] bytes = ObjectCastUtils.castToByteArray(value); - return new SqlTypeValue() { - @Override - public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, - String typeName) throws SQLException { - if (null != bytes) { - InputStream is = new ByteArrayInputStream(bytes); - ps.setBlob(paramIndex, is); - iss.add(is); - } else { - ps.setNull(paramIndex, sqlType); + try { + switch (jdbcType) { + case Types.NUMERIC: + return new SqlTypeValue() { + @Override + public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) + throws SQLException { + if (value instanceof Byte) { + ps.setByte(paramIndex, (Byte) value); + } else if (value instanceof Short) { + ps.setShort(paramIndex, (Short) value); + } else if (value instanceof Integer) { + ps.setInt(paramIndex, (Integer) value); + } else if (value instanceof BigInteger) { + ps.setInt(paramIndex, ((BigInteger) value).intValue()); + } else if (value instanceof Long) { + ps.setLong(paramIndex, (Long) value); + } else if (value instanceof Float) { + ps.setFloat(paramIndex, (Float) value); + } else if (value instanceof Double) { + ps.setDouble(paramIndex, (Double) value); + } else if (value instanceof BigDecimal) { + ps.setBigDecimal(paramIndex, (BigDecimal) value); + } else { + ps.setObject(paramIndex, value, sqlType); + } } - } - }; - } catch (Exception e) { - log.warn("Convert from {} to Oracle BLOB failed:{}", value.getClass().getName(), e.getMessage()); - return null; + }; + case Types.TIME: + return new SqlTypeValue() { + @Override + public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) + throws SQLException { + if (value instanceof Time) { + ps.setTime(paramIndex, (Time) value); + } else { + LocalTime time = ObjectCastUtils.castToLocalTime(value); + if (null == time) { + ps.setNull(paramIndex, jdbcType); + } else { + ps.setTime(paramIndex, Time.valueOf(time)); + } + } + } + }; + case Types.DATE: + return new SqlTypeValue() { + @Override + public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) + throws SQLException { + if (value instanceof Date) { + ps.setDate(paramIndex, (Date) value); + } else { + LocalDate date = ObjectCastUtils.castToLocalDate(value); + if (null == date) { + ps.setNull(paramIndex, jdbcType); + } else { + ps.setDate(paramIndex, Date.valueOf(date)); + } + } + } + }; + case Types.TIMESTAMP: + return new SqlTypeValue() { + @Override + public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) + throws SQLException { + if (value instanceof Timestamp) { + ps.setTimestamp(paramIndex, (Timestamp) value); + } else { + LocalDateTime dateTime = ObjectCastUtils.castToLocalDateTime(value); + if (null == dateTime) { + ps.setNull(paramIndex, jdbcType); + } else { + ps.setTimestamp(paramIndex, Timestamp.valueOf(dateTime)); + } + } + } + }; + case Types.BLOB: + return new SqlTypeValue() { + @Override + public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) + throws SQLException { + if (value instanceof java.sql.Blob) { + ps.setBlob(paramIndex, (java.sql.Blob) value); + } else { + InputStream is = new ByteArrayInputStream(ObjectCastUtils.castToByteArray(value)); + ps.setBlob(paramIndex, is); + iss.add(is); + } + } + }; + case Types.CLOB: + return new SqlTypeValue() { + @Override + public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) + throws SQLException { + if (value instanceof java.sql.Clob) { + ps.setClob(paramIndex, (java.sql.Clob) value); + } else { + java.io.Reader reader = new StringReader(ObjectCastUtils.castToString(value)); + ps.setClob(paramIndex, reader); + } + } + }; + case Types.NCLOB: + return new SqlTypeValue() { + @Override + public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) + throws SQLException { + if (value instanceof java.sql.NClob) { + ps.setNClob(paramIndex, (java.sql.NClob) value); + } else { + java.io.Reader reader = new StringReader(ObjectCastUtils.castToString(value)); + ps.setNClob(paramIndex, reader); + } + } + }; + default: + return ObjectCastUtils.castByJdbcType(jdbcType, value); } + } catch ( + Exception e) { + log.warn("Convert from {} to Oracle {} failed: {}", + value.getClass().getName(), + JdbcTypesUtils.resolveTypeName(jdbcType), + e.getMessage() + ); + return null; } - return ObjectCastUtils.castByJdbcType(jdbcType, value); } } diff --git a/dbswitch-product/dbswitch-product-postgresql/src/main/java/com/gitee/dbswitch/product/postgresql/PostgresTableCopyWriteProvider.java b/dbswitch-product/dbswitch-product-postgresql/src/main/java/com/gitee/dbswitch/product/postgresql/PostgresTableCopyWriteProvider.java index 66828b01..d445260e 100644 --- a/dbswitch-product/dbswitch-product-postgresql/src/main/java/com/gitee/dbswitch/product/postgresql/PostgresTableCopyWriteProvider.java +++ b/dbswitch-product/dbswitch-product-postgresql/src/main/java/com/gitee/dbswitch/product/postgresql/PostgresTableCopyWriteProvider.java @@ -286,7 +286,11 @@ public class PostgresTableCopyWriteProvider extends DefaultTableDataWriteProvide case Types.BOOLEAN: case Types.BIT: if (null == fieldValue) { - row.setBoolean(i, null); + if (Types.BIT == fieldType) { + row.setByte(i, null); + } else { + row.setBoolean(i, null); + } } else { Boolean val = null; try { @@ -301,7 +305,11 @@ public class PostgresTableCopyWriteProvider extends DefaultTableDataWriteProvide "表名[%s.%s]的字段名[%s]数据类型错误,应该为java.lang.Boolean,而实际的数据类型为%s", schemaName, tableName, fieldName, fieldValue.getClass().getName())); } - row.setBoolean(i, val); + if (Types.BIT == fieldType) { + row.setByte(i, val ? Byte.valueOf((byte) 1) : Byte.valueOf((byte) 0)); + } else { + row.setBoolean(i, val); + } } break; case Types.TIME: