开发者社区> 问答> 正文

CanalAdapter两张表join字段同名bug

环境信息

canal version canal-adapter1.1.3-alpha-2 mysql version mysql

问题描述

在两张表join的时候,如果两张表有同名列名,在update的时候,会错误更新两个字段 表A:school_phase 有一个字段是phase_code 表B:school 有一个字段也是phase_code 表A和表B left join的时候 把表a的phase_code 命名为 school_phase_code,表b的还是phase_code 但是测试在测试的过程发现,更新表A的phase_code 会也把es索引里的phase_code也更新了

现执行情况

这边看了下代码是

public Object getESDataFromDmlData(ESMapping mapping, Map<String, Object> dmlData, Map<String, Object> dmlOld, Map<String, Object> esFieldData)

这个函数有问题,里面

if (dmlOld.containsKey(columnName) && !mapping.getSkips().contains(fieldItem.getFieldName())) { esFieldData.put(Util.cleanColumn(fieldItem.getFieldName()), getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName)); }

没有考虑表的关系,我这边是重新了这段逻辑,在判断更新的时候,加了一段判断是否是同一张表

//处理两张表字段同名的bug String aliceTableName = mapping.getSchemaItem().getAliasesName(tableName); if(StringUtils.isNotEmpty(aliceTableName)){

       for ( Map.Entry<String, FieldItem> fieldItemEntry : schemaItem.getSelectFields().entrySet()) {
           FieldItem fieldItem =fieldItemEntry.getValue();
           String ownTable = fieldItem.getOwner();
           String columnName = fieldItem.getColumnItems().iterator().next().getColumnName();
           String aliceColumnName = fieldItemEntry.getKey();

           if (fieldItem.getFieldName().equals(idFieldName)) {
               resultIdVal = getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName);
           }

           if (StringUtils.equals(ownTable,aliceTableName) && dmlOld.get(columnName) != null && !mapping.getSkips().contains(fieldItem.getFieldName())) {
               esFieldData.put(fieldItem.getFieldName(),
                       getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName));
           }
       }
   }else{
       for (FieldItem fieldItem : schemaItem.getSelectFields().values()) {
           String columnName = fieldItem.getColumnItems().iterator().next().getColumnName();

           if (fieldItem.getFieldName().equals(idFieldName)) {
               resultIdVal = getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName);
           }

           if (dmlOld.get(columnName) != null && !mapping.getSkips().contains(fieldItem.getFieldName())) {
               esFieldData.put(fieldItem.getFieldName(),
                       getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName));
           }
       }
   }

    return resultIdVal;
}

原提问者GitHub用户house0128

展开
收起
古拉古拉 2023-05-08 12:19:33 106 0
2 条回答
写回答
取消 提交回答
  • join后sql里需要设置字段别名,不能同名

    原回答者GitHub用户rewerma

    2023-05-09 16:18:51
    赞同 展开评论 打赏
  • 针对这个问题,建议尝试以下几种解决方案:

    修改表结构,避免同名字段 根据您的描述,表 A 和表 B 中均有一个同名的 phase_code 字段。建议修改表结构,将这两个字段命名为不同的名称,避免出现同名字段导致的数据更新错误。

    修改 CanalAdapter 代码,增加字段前缀或后缀 如果您无法修改表结构,可以考虑修改 CanalAdapter 的代码,在处理 join 操作时增加相应的字段前缀或后缀。例如,可以将表 A 的 phase_code 字段更名为 school_phase_code,将表 B 的 phase_code 字段更名为 school_code_phase 等。这样可以确保每个字段的唯一性,避免出现同名字段导致的更新错误。

    使用 Canal 的 interceptors 功能 Canal 提供了 interceptors 功能,可以在数据变更事件到达 Canal 内部之前对其进行拦截和修改。您可以编写一个自定义的 interceptor,在处理 join 操作时检测同名字段,并将其更名为唯一的名称。例如,可以将表 A 的 phase_code 字段更名为 school_phase_code,将表 B 的 phase_code 字段更名为 school_code_phase 等。

    2023-05-08 16:12:32
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载