基于GPT代码提示,辅助实现datax hdfswriter 支持decimal类型,并且支持定义precision, scale

在参考相关博客,完成datax hdfswriter支持decimal类型需求后,在数据使用时发现了新的问题:

写出的orc文件中,decimal类型字段序列化时使用的是默认精度:decimal(38,18)。在某些情况下,会触发精度转换异常的报错,导致计算任务失败。

java 复制代码
public class HiveDecimal implements Comparable<HiveDecimal> {
  public static final int SYSTEM_DEFAULT_PRECISION = 38;
  public static final int SYSTEM_DEFAULT_SCALE = 18;
}
markdown 复制代码
df.printSchema()
root
 |-- xxxx_rate: decimal(38,18) (nullable = true)

因此需要在hdfswriter写出orc文件时,为decimal字段指定需要的precision和scale。

查看了decimal默认的objectInspector的实例化逻辑,并没有传入decimal字段的precision和scale的参数位置。

java 复制代码
import org.apache.hadoop.hive.common.type.HiveDecimal;
...

public List<ObjectInspector>  getColumnTypeInspectors(List<Configuration> columns){
    ...
    
    case DECIMAL:
        // 创建DECIMAL的objectInspector
        objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(HiveDecimal.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
        break;
}

深入ObjectInspectorFactory.getReflectionObjectInspector中,查看Inspector的实例化逻辑

java 复制代码
public static ObjectInspector getReflectionObjectInspector(Type t, ObjectInspectorOptions options) {
  ObjectInspector oi = (ObjectInspector)objectInspectorCache.get(t);
  if (oi == null) {
    // 没有缓存,进行第一次实例化
    oi = getReflectionObjectInspectorNoCache(t, options);
    objectInspectorCache.put(t, oi);
  }

  ...
  return oi;
}

第一次实例化,根据指定数据的class类型,实例化对应的objectInspector

java 复制代码
private static ObjectInspector getReflectionObjectInspectorNoCache(Type t, ObjectInspectorOptions options) {
    ...
    
    if (!(t instanceof Class)) {
    throw new RuntimeException(ObjectInspectorFactory.class.getName() + " internal error:" + t);
    } else {
    Class<?> c = (Class)t;
    if (PrimitiveObjectInspectorUtils.isPrimitiveJavaType(c)) {
      return PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(PrimitiveObjectInspectorUtils.getTypeEntryFromPrimitiveJavaType(c).primitiveCategory);
    } else if (PrimitiveObjectInspectorUtils.isPrimitiveJavaClass(c)) {
      // org.apache.hadoop.hive.common.type.HiveDecimal 
      // 不属于基本数据类型或对应的装箱类型,只是原始Java类
      // 并且没有支持自定义精度传参的迹象,需要另寻他法。
      return PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(PrimitiveObjectInspectorUtils.getTypeEntryFromPrimitiveJavaClass(c).primitiveCategory);
    } else if (PrimitiveObjectInspectorUtils.isPrimitiveWritableClass(c)) {
      return PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(PrimitiveObjectInspectorUtils.getTypeEntryFromPrimitiveWritableClass(c).primitiveCategory);
    } else if (Enum.class.isAssignableFrom(c)) {
      return PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(PrimitiveCategory.STRING);
    } else {
        ...
    }

}

在查询gpt后,得到了潜在的实现逻辑,根据这个实现建议,测试一下下:

java 复制代码
import org.apache.hadoop.hive.common.type.HiveDecimal;
...

public List<ObjectInspector>  getColumnTypeInspectors(List<Configuration> columns){
    ...
    
    case DECIMAL:
        // 创建支持自定义精度的DECIMAL的objectInspector
        Integer precision = eachColumnConf.getInt(Key.PRECISION);
        Integer scale = eachColumnConf.getInt(Key.SCALE);
        DecimalTypeInfo typeInfo = TypeInfoFactory.getDecimalTypeInfo(precision, scale);
        objectInspector = PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(typeInfo);
        break;
}

重新编译打包后,测试指定decimal的precision和scale的job.json。

json 复制代码
{
    ...
    "writeMode": "append",
    "column": [
        {
            "type": "decimal",
            "name": "xxxx_rate",
            "precision": 38,
            "scale": 0
        },
        ...
    ]
}

测试spark读取写出的orc文件,查看对应字段的schema信息,读取到了正确的精度信息,问题解决:

markdown 复制代码
df = spark.read.orc("/user/hive/warehouse/xxxx/xxxxx")

df.printSchema()
root
 |-- xxxx_rate: decimal(38,0) (nullable = true)

参考文章:

1、blog.csdn.net/Shadow_Ligh...

2、github.com/alibaba/Dat...

3、github.com/alibaba/Dat...

相关推荐
dd聊技术17 分钟前
Prompt 从代码常量搬进数据库:改一句话,不用再发一次版
java·后端
ly768921 分钟前
Spring Data MongoDB 批量写入实践:BulkOperations 批大小与写关注权衡
java·mongodb·spring·批量写入·bulkoperations·写关注·mongotemplate
TAKETODAY27 分钟前
Netty 是怎么把 HTTP 请求交给 MVC 的?一个请求的完整链路
java·netty
蚰蜒螟32 分钟前
从内核启动到 Java NIO:一次 socket accept 的源码串联之旅
java·python·nio
野生数据人33 分钟前
日志平台每日巡检我压成了一条命令链:从 SHOW BACKENDS 到扩缩容的完整步骤
大数据·数据库
会编程的吕洞宾1 小时前
Spring AI 2.0 自校正结构化输出实战:让大模型稳定吐出合格 JSON
java·人工智能·后端·spring
bmxy小明同学1 小时前
2026-10-09-token与成本
java·前端·数据库
liliangcsdn1 小时前
如何用DrissionPage进行网页自动化
大数据·自动化
野生数据人1 小时前
日志平台运维速查表:巡检命令、阈值、参数,一张贴墙
大数据·数据库
微笑的曙光2 小时前
第八期 · 情绪闯关挑战盒:一个「无惩罚」成就系统的数据建模
java·微信小程序