使用 Java 8 日期作为逻辑类型的 Avro

Posted

技术标签:

【中文标题】使用 Java 8 日期作为逻辑类型的 Avro【英文标题】:Avro with Java 8 dates as logical type 【发布时间】:2018-01-24 12:26:26 【问题描述】:

最新的 Avro 编译器 (1.8.2) 使用基于 Joda-Time 的实现为 dates logical types 生成 java 源代码。如何配置 Avro 编译器以生成使用 Java 8 日期时间 API 的源代码?

【问题讨论】:

【参考方案1】:

你需要创建自己的Conversions 来支持java-8 date-time api,下面是java.time.LocalDate的转换:

class Java8LocalDateConversion extends Conversion<LocalDate> 
    @Override
    public Class<LocalDate> getConvertedType() 
        return LocalDate.class;
    

    @Override
    public String getLogicalTypeName() 
        //      v--- reuse the logical type `date`
        return "date";
    

    @Override
    // convert LocalDate to Integer
    public Integer toInt(LocalDate value, Schema schema, LogicalType type) 
        return (int) value.toEpochDay();
    

    @Override
    // parse LocalDate from Integer
    public LocalDate fromInt(Integer value, Schema schema, LogicalType type) 
        return LocalDate.ofEpochDay(value);
    

逻辑类型可以在avro中复用,所以可以使用已有的date逻辑类型,例如:

Schema schema = LogicalTypes.date().addToSchema(Schema.create(Type.INT));

对于序列化和反序列化,您应该设置GenericData,它将找到您自己的转换,例如:

//serializing
DatumWriter<T> out = new SpecificDatumWriter<>(schema, data());

// deserializing
DatumReader<T> in = new SpecificDatumReader<>(schema, schema, data());

private SpecificData data() 
    SpecificData it = new SpecificData();
    it.addLogicalTypeConversion(new Java8LocalDateConversion());
    return it;

如果不想每次都配置GenericData,可以改用全局GenericData,例如:

//      register the conversion globally ---v
SpecificData.get().addLogicalTypeConversion(new Java8LocalDateConversion());

【讨论】:

这是我在github上写的测试。 解决不了问题:avro编译器还是会为这种逻辑类型生成joda的日期字段。【参考方案2】:

目前(avro 1.8.2)这是不可能的。生成 Joda 日期/时间类是硬编码的。

当前的master 分支已切换到Java 8,并且有一个open issue(带有Pull Request)可以添加使用java.time.* 类型生成类的功能。

不幸的是,我不知道master 中的任何内容的发布时间表。如果你喜欢冒险,你可以将补丁应用到1.8.2,因为理论上它应该都是兼容的。序列化/反序列化时的底层基本类型仍然是整数和长整数。

【讨论】:

【参考方案3】:

使用 avro 1.9.2,您可以使用例如date 为LocalDate

    
      "name": "Transaction",
      "type": "record",
      "fields": [
        
          "name": "time",
          "type": 
            "type": "int",
            "logicalType": "date"
          
        ,

其他类型请参见Logical Types。

【讨论】:

【参考方案4】:

AVRO 1.10 添加了对LocalDateTime 的支持,请参阅Apache Avro™ 1.10.0 Specification

【讨论】:

您引用的页面上没有“LocalDateTime”之类的东西。 我的意思是“本地时间戳”。这被翻译成 Java 中的“LocalDateTime”。【参考方案5】: 使用 Avro 版本 >= 1.9.0 1.10.0 添加&lt;dateTimeLogicalTypeImplementation&gt;jsr310&lt;/dateTimeLogicalTypeImplementation&gt; 进入configuration 部分。 使用 Avro 版本 >= 1.10.0 java8/jsr310 默认使用的原生日期/时间类

【讨论】:

【参考方案6】:

Avro v1.11.0 TimeConversions 已内置支持 Java 8 时间类,例如 LocalDateInstant 等。TimeConversions 必须注册到 SpecificData 类或其子类,例如 @ 987654326@。下面是如何使用TimeConversions的完整示例:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account

    private int id;
    private LocalDate createdOn;

    public static void main(String[] args) throws IOException
    
        // create schema from POJO Account class
        ReflectData reflectData = ReflectData.get();
        reflectData.addLogicalTypeConversion(new TimeConversions.DateConversion());
        Schema schema = reflectData.getSchema(Account.class);
        System.out.println("Schema in JSON:\n" + schema + "\n");

        // create avro writer
        DatumWriter<Account> datumWriter = new ReflectDatumWriter<>(schema);
        DataFileWriter<Account> dataFileWriter = new DataFileWriter<>(datumWriter);
        // dataFileWriter.setCodec(CodecFactory.snappyCodec()); // for compression
        dataFileWriter.create(schema, new File("accounts.avro"));
        dataFileWriter.append(new Account(123, LocalDate.of(2001, 1, 1)));
        dataFileWriter.append(new Account(234, LocalDate.of(2002, 2, 2)));
        dataFileWriter.close();

        // create avro reader
        DatumReader<Account> datumReader = new ReflectDatumReader<>(schema);
        DataFileReader<Account> dataFileReader = new DataFileReader<>(new File("accounts.avro"), datumReader);
        dataFileReader.forEach(System.out::println);
    

【讨论】:

以上是关于使用 Java 8 日期作为逻辑类型的 Avro的主要内容,如果未能解决你的问题,请参考以下文章

avro 格式的日期数据类型的 Sqoop 导入

具有逻辑类型的 Avro 模式不能与最新的 confluent-kafka 一起使用

将 Avro 中存储为整数(自 1970 年 1 月 1 日以来的天数)的“日期”转换为雪花“日期”类型

avro序列化详细操作

将 Java 8 中两个日期的差值(天数)作为 Short 原始类型的最简单方法

如何在Avro Union逻辑类型字段中指定转换器的默认值?