From 711971599ff0e1b433045804f64646a120e14300 Mon Sep 17 00:00:00 2001 From: wbxlll Date: Fri, 8 Mar 2024 11:50:53 +0800 Subject: [PATCH] =?UTF-8?q?fix=20issue=20client-adapter.rdb=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=9C=A8=E8=BD=AC=E6=8D=A2=E6=97=B6=E9=97=B4=E6=97=B6?= =?UTF-8?q?=E4=B8=A2=E5=A4=B1=E7=B2=BE=E5=BA=A6=20#5052=20(#5053)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix the issue that client-adapter.rdb module loses precision when converting microsencond time * fix the issue that client-adapter.rdb module loses precision when converting to java.sql.Time or java.sql.Timestamp * fix the issue that client-adapter.rdb module loses precision when converting to java.sql.Time or java.sql.Timestamp --- .../canal/client/adapter/support/Util.java | 49 +++++++++++++++++++ .../client/adapter/rdb/support/SyncUtil.java | 37 ++++++++++---- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Util.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Util.java index e8197067..7bac6c5f 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Util.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Util.java @@ -327,4 +327,53 @@ public class Util { return null; } + + /** + * Check if the time string has millisecond or microsecond units + * @param timeStr time string + * @return boolean + */ + public static boolean isAccuracyOverSecond(String timeStr){ + if (StringUtils.isEmpty(timeStr)) { + return false; + } + String[] times = StringUtils.split(timeStr, "."); + return times.length > 1 && !times[times.length - 1].isEmpty(); + } + + + /** + * Check if the datetime string has microsecond or nanosecond units + * @param datetimeStr datetime string + * @return boolean + */ + public static boolean isAccuracyOverMillisecond(String datetimeStr){ + if (StringUtils.isEmpty(datetimeStr)) { + return false; + } + String[] times = StringUtils.split(datetimeStr, "."); + return times.length > 1 && times[times.length - 1].length() > 3; + } + + + /** + * MySQL has fractional seconds support for TIME, DATETIME, and TIMESTAMP values, with up to microseconds (6 digits) precision. + * ISO-8601 standard format is with up to nanoseconds (9 digits) precision, which is sufficient for storing MySQL time-related data. + * @param datetimeStr datetime string + * @return LocalDateTime + */ + public static LocalDateTime parseISOLocalDateTime(String datetimeStr) { + if (StringUtils.isEmpty(datetimeStr)) { + return null; + } + datetimeStr = datetimeStr.trim(); + try { + //Replace SPACE in middle with `T` to comply with ISO-8601 standard format + return LocalDateTime.parse(datetimeStr.replace(" ", "T")); + }catch (Exception e){ + logger.error("Convert datetime string to ISOLocalDateTime fail:{}", datetimeStr, e); + } + return null; + } + } diff --git a/client-adapter/rdb/src/main/java/com/alibaba/otter/canal/client/adapter/rdb/support/SyncUtil.java b/client-adapter/rdb/src/main/java/com/alibaba/otter/canal/client/adapter/rdb/support/SyncUtil.java index c5fb4d5b..c86d7eda 100644 --- a/client-adapter/rdb/src/main/java/com/alibaba/otter/canal/client/adapter/rdb/support/SyncUtil.java +++ b/client-adapter/rdb/src/main/java/com/alibaba/otter/canal/client/adapter/rdb/support/SyncUtil.java @@ -12,6 +12,7 @@ import java.io.StringReader; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.sql.*; +import java.time.LocalDateTime; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; @@ -220,11 +221,16 @@ public class SyncUtil { pstmt.setTime(i, new java.sql.Time(((java.util.Date) value).getTime())); } else if (value instanceof String) { String v = (String) value; - java.util.Date date = Util.parseDate(v); - if (date != null) { - pstmt.setTime(i, new Time(date.getTime())); - } else { - pstmt.setNull(i, type); + if(Util.isAccuracyOverSecond(v)) { + //the java.sql.time doesn't support for even millisecond, only setObject works here. + pstmt.setObject(i, v); + }else { + java.util.Date date = Util.parseDate(v); + if (date != null) { + pstmt.setTime(i, new Time(date.getTime())); + } else { + pstmt.setNull(i, type); + } } } else { pstmt.setNull(i, type); @@ -238,11 +244,22 @@ public class SyncUtil { } else if (value instanceof String) { String v = (String) value; if (!v.startsWith("0000-00-00")) { - java.util.Date date = Util.parseDate(v); - if (date != null) { - pstmt.setTimestamp(i, new Timestamp(date.getTime())); - } else { - pstmt.setNull(i, type); + if(Util.isAccuracyOverMillisecond(v)){ + //convert to ISO-8601 standard format, with up to nanoseconds (9 digits) precision + LocalDateTime isoDatetime = Util.parseISOLocalDateTime(v); + if (isoDatetime != null) { + pstmt.setTimestamp(i, Timestamp.valueOf(isoDatetime)); + } else { + //if can't convert, set to null + pstmt.setNull(i, type); + } + }else { + java.util.Date date = Util.parseDate(v); + if (date != null) { + pstmt.setTimestamp(i, new Timestamp(date.getTime())); + } else { + pstmt.setNull(i, type); + } } } else { pstmt.setObject(i, value);