This is an automated email from the ASF dual-hosted git repository.

jimin pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git


The following commit(s) were added to refs/heads/2.x by this push:
     new 9f39706fb8 bugfix: unify DmdbTimestamp comparison via UTC Instant 
(#7538)
9f39706fb8 is described below

commit 9f39706fb81a4dc48092ebf79c5e153aedecf3b3
Author: xiaoyu <[email protected]>
AuthorDate: Wed Jul 23 10:29:38 2025 +0800

    bugfix: unify DmdbTimestamp comparison via UTC Instant (#7538)
---
 changes/en-us/2.x.md                               |  7 +++--
 changes/zh-cn/2.x.md                               |  7 +++--
 .../seata/rm/datasource/DataCompareUtils.java      | 34 +++++++++++++++++++++
 .../undo/parser/JacksonUndoLogParserTest.java      | 35 ++++++++++++++++++----
 4 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index 175b5979b6..825833aa5f 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -25,6 +25,7 @@ Add changes here for all PR submitted to the 2.x branch.
 
 ### bugfix:
 
+- [[#7538](https://github.com/apache/incubator-seata/pull/7538)] unify 
DmdbTimestamp comparison via UTC Instant to prevent rollback failure
 - [[#7546](https://github.com/seata/seata/pull/7546)] fix client spring 
version compatible
 
 ### optimize:
@@ -52,13 +53,13 @@ Add changes here for all PR submitted to the 2.x branch.
 - [[#7531](https://github.com/seata/seata/pull/7531)] Optimize the Readme and 
change documents
 
 
-非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。
+Thanks to these contributors for their code commits. Please report an 
unintended omission.
 
-<!-- 请确保您的 GitHub ID 在以下列表中 -->
+<!-- Please make sure your Github ID is in the list below -->
 
 - [slievrly](https://github.com/slievrly)
 - [YvCeung](https://github.com/YvCeung)
 - [xjlgod](https://github.com/xjlgod)
 
 
-同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。
+Also, we receive many valuable issues, questions and advices from our 
community. Thanks for you all.
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index f9aaef42bc..a167c7e1d0 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -25,6 +25,7 @@
 
 ### bugfix:
 
+- [[#7538](https://github.com/apache/incubator-seata/pull/7538)] 
统一DmdbTimestamp比较方式,通过UTC比较,以防止回滚失败
 - [[#7546](https://github.com/seata/seata/pull/7546)] 修复客户端spring版本兼容
 
 
@@ -51,13 +52,13 @@
 - [[#7531](https://github.com/seata/seata/pull/7531)] 优化 Readme 和 change 文档
 
 
-Thanks to these contributors for their code commits. Please report an 
unintended omission.
+非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。
 
-<!-- Please make sure your Github ID is in the list below -->
+<!-- 请确保您的 GitHub ID 在以下列表中 -->
 
 - [slievrly](https://github.com/slievrly)
 - [YvCeung](https://github.com/YvCeung)
 - [xjlgod](https://github.com/xjlgod)
 
 
-Also, we receive many valuable issues, questions and advices from our 
community. Thanks for you all.
+同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。
diff --git 
a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java
 
b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java
index f20a0870a6..bd553a7be3 100644
--- 
a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java
+++ 
b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java
@@ -24,13 +24,16 @@ import org.apache.seata.rm.datasource.sql.struct.Row;
 import org.apache.seata.rm.datasource.sql.struct.TableRecords;
 import org.apache.seata.rm.datasource.undo.AbstractUndoLogManager;
 import org.apache.seata.rm.datasource.undo.parser.FastjsonUndoLogParser;
+import org.apache.seata.rm.datasource.undo.parser.JacksonUndoLogParser;
 import org.apache.seata.sqlparser.struct.TableMeta;
 
+import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.sql.Date;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
+import java.time.Instant;
 import java.time.LocalDateTime;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -73,6 +76,23 @@ public class DataCompareUtils {
                             if (StringUtils.equals(currentSerializer, 
FastjsonUndoLogParser.NAME)) {
                                 convertType(f0, f1);
                             }
+                            if (StringUtils.equals(currentSerializer, 
JacksonUndoLogParser.NAME)) {
+                                Object v0 = f0.getValue();
+                                Object v1 = f1.getValue();
+                                if (isDmdbTimestamp(v0) && 
isDmdbTimestamp(v1)) {
+                                    Instant i0 = toInstant(v0);
+                                    Instant i1 = toInstant(v1);
+                                    boolean equals = Objects.equals(i0, i1);
+                                    return equals
+                                            ? Result.ok()
+                                            : Result.buildWithParams(
+                                                    false,
+                                                    "Field not equals 
(DmdbTimestamp), name {}, old value {}, new value {}",
+                                                    f0.getName(),
+                                                    v0,
+                                                    v1);
+                                }
+                            }
                             boolean result = Objects.deepEquals(f0.getValue(), 
f1.getValue());
                             if (result) {
                                 return Result.ok();
@@ -249,4 +269,18 @@ public class DataCompareUtils {
         }
         return rowMap;
     }
+
+    private static boolean isDmdbTimestamp(Object obj) {
+        return obj != null
+                && 
"dm.jdbc.driver.DmdbTimestamp".equals(obj.getClass().getName());
+    }
+
+    private static Instant toInstant(Object dmdbTimestamp) {
+        try {
+            Method toInstantMethod = 
dmdbTimestamp.getClass().getMethod("toInstant");
+            return (Instant) toInstantMethod.invoke(dmdbTimestamp);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to convert DmdbTimestamp to 
Instant", e);
+        }
+    }
 }
diff --git 
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/JacksonUndoLogParserTest.java
 
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/JacksonUndoLogParserTest.java
index c74e71d9c3..37e804fc72 100644
--- 
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/JacksonUndoLogParserTest.java
+++ 
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/JacksonUndoLogParserTest.java
@@ -20,10 +20,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.seata.common.loader.EnhancedServiceLoader;
 import org.apache.seata.rm.datasource.DataCompareUtils;
 import org.apache.seata.rm.datasource.sql.struct.Field;
+import org.apache.seata.rm.datasource.undo.AbstractUndoLogManager;
 import org.apache.seata.rm.datasource.undo.BaseUndoLogParserTest;
 import org.apache.seata.rm.datasource.undo.UndoLogParser;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
 
 import javax.sql.rowset.serial.SerialBlob;
 import javax.sql.rowset.serial.SerialClob;
@@ -40,6 +42,8 @@ import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.Date;
 
+import static org.mockito.Mockito.mockStatic;
+
 public class JacksonUndoLogParserTest extends BaseUndoLogParserTest {
 
     JacksonUndoLogParser parser =
@@ -149,12 +153,31 @@ public class JacksonUndoLogParserTest extends 
BaseUndoLogParserTest {
         Assertions.assertTrue(DataCompareUtils.isFieldEquals(field, 
sameField).getResult());
     }
 
-    private boolean checkClassExists(String className) {
-        try {
-            Class.forName(className);
-            return true;
-        } catch (ClassNotFoundException e) {
-            return false;
+    @Test
+    public void testSerializeAndDeserializeDmdbTimestampWithNoZone()
+            throws NoSuchFieldException, IllegalAccessException, 
ClassNotFoundException, NoSuchMethodException,
+                    InvocationTargetException, IOException {
+
+        try (MockedStatic<AbstractUndoLogManager> mockedStatic = 
mockStatic(AbstractUndoLogManager.class)) {
+            
mockedStatic.when(AbstractUndoLogManager::getCurrentSerializer).thenReturn(JacksonUndoLogParser.NAME);
+
+            java.lang.reflect.Field reflectField = 
parser.getClass().getDeclaredField("mapper");
+            reflectField.setAccessible(true);
+            ObjectMapper mapper = (ObjectMapper) reflectField.get(parser);
+
+            Class<?> dmdbTimestampClass = 
Class.forName("dm.jdbc.driver.DmdbTimestamp");
+            Method valueOfDateMethod = dmdbTimestampClass.getMethod("valueOf", 
Date.class);
+
+            Object originalTimestamp = valueOfDateMethod.invoke(null, new 
Date(1721985847000L));
+            Object originalTimestamp2 = valueOfDateMethod.invoke(null, new 
Date(1721985847001L));
+            Field field = new Field("dmdb_timestamp_type", 
JDBCType.TIMESTAMP.getVendorTypeNumber(), originalTimestamp);
+            Field field2 =
+                    new Field("dmdb_timestamp_type", 
JDBCType.TIMESTAMP.getVendorTypeNumber(), originalTimestamp2);
+            byte[] bytes = mapper.writeValueAsBytes(field);
+            Field sameField = mapper.readValue(bytes, Field.class);
+            Assertions.assertTrue(
+                    DataCompareUtils.isFieldEquals(field, 
sameField).getResult());
+            Assertions.assertFalse(DataCompareUtils.isFieldEquals(field, 
field2).getResult());
         }
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to