具有 Java 8 个日期作为逻辑类型的 Avro
Avro with Java 8 dates as logical type
最新的 Avro 编译器 (1.8.2) 为基于 dates logical types with Joda-Time 的实现生成 java 源代码。如何配置 Avro 编译器以生成使用 Java 8 日期时间 API 的源?
您需要创建自己的 Conversions 以支持 java-8 日期时间 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());
目前(avro 1.8.2)这是不可能的。它被硬编码生成 Joda date/time 类。
当前的 master
分支已经切换到 Java 8 并且有一个 open issue (with Pull Request) 添加生成 类 和 java.time.*
类型的能力.
不幸的是,我不知道 master
当前的任何类型的发布时间表。如果你喜欢冒险,你可以将补丁应用到 1.8.2
,因为理论上它应该都是兼容的。序列化/反序列化时的底层基本类型仍然是整数和长整数。
对于 avro 1.9.2,您可以使用例如date 对于 LocalDate
:
{
"name": "Transaction",
"type": "record",
"fields": [
{
"name": "time",
"type": {
"type": "int",
"logicalType": "date"
}
},
其他类型见Logical Types。
AVRO 1.10 添加了对 LocalDateTime
的支持,请参阅 Apache Avro™ 1.10.0 Specification
- 使用 Avro 版本 >=
1.9.0
< 1.10.0
- 添加
<dateTimeLogicalTypeImplementation>jsr310</dateTimeLogicalTypeImplementation>
进入 configuration
部分。
- 使用 Avro 版本 >=
1.10.0
- java8/jsr310原生date/time类默认使用
Avro v1.11.0 TimeConversions 内置支持 Java 8 次 class,例如 LocalDate
、Instant
等。TimeConversions
必须注册 SpecificData
class 或其子 class,例如 ReflectData
。这是如何使用 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);
}
}
最新的 Avro 编译器 (1.8.2) 为基于 dates logical types with Joda-Time 的实现生成 java 源代码。如何配置 Avro 编译器以生成使用 Java 8 日期时间 API 的源?
您需要创建自己的 Conversions 以支持 java-8 日期时间 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());
目前(avro 1.8.2)这是不可能的。它被硬编码生成 Joda date/time 类。
当前的 master
分支已经切换到 Java 8 并且有一个 open issue (with Pull Request) 添加生成 类 和 java.time.*
类型的能力.
不幸的是,我不知道 master
当前的任何类型的发布时间表。如果你喜欢冒险,你可以将补丁应用到 1.8.2
,因为理论上它应该都是兼容的。序列化/反序列化时的底层基本类型仍然是整数和长整数。
对于 avro 1.9.2,您可以使用例如date 对于 LocalDate
:
{
"name": "Transaction",
"type": "record",
"fields": [
{
"name": "time",
"type": {
"type": "int",
"logicalType": "date"
}
},
其他类型见Logical Types。
AVRO 1.10 添加了对 LocalDateTime
的支持,请参阅 Apache Avro™ 1.10.0 Specification
- 使用 Avro 版本 >=
1.9.0
<1.10.0
- 添加
<dateTimeLogicalTypeImplementation>jsr310</dateTimeLogicalTypeImplementation>
进入configuration
部分。 - 使用 Avro 版本 >=
1.10.0
- java8/jsr310原生date/time类默认使用
Avro v1.11.0 TimeConversions 内置支持 Java 8 次 class,例如 LocalDate
、Instant
等。TimeConversions
必须注册 SpecificData
class 或其子 class,例如 ReflectData
。这是如何使用 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);
}
}