Cassandra 的Custom Codecs

mapper可以利用自定义编解码器将自定义转换映射到 columns和fields.

Declaring codecs

假定有一张表,含有timestamp column:

create table user(id int primary key, birth timestamp);

还create了一个custom class, 一个codec 来 vonvert it:

public class MyCustomDate { ... }

public class MyCustomDateCodec extends TypeCodec<MyCustomDate> {
    public MyCustomDate() {
        super(DataType.timestamp(), MyCustomDate.class);
    }
    ...
}

 

Pre-registered codecs

如果你register codec with the mapper's underlying Cluster, it will be automatically available to the mapper:

Cluster cluster = Cluster.builder()
    .addContactPoint("127.0.0.1")
    .withCodecRegistry(
        new CodecRegistry().register(new MyCustomDateCodec())
    ).build();

MappingManager mappingManager = new MappingManager(cluster.connect());

你可以正常的使用自定义的type来  create mapped类, 而不需要额外的配置:

@Table(name = "user")
public class User {
  @PartitionKey
  private int id;
  private MyCustomDate birth;

  ... // getters and setters
}

 

This also works in accessors:

@Accessor
interface UserAccessor {
  @Query("update user set birth = :b where id = :i")
  void updateBirth(@Param("i") int id,
                   @Param("b") MyCustomDate birth);
}

 

One-time declaration

有时只需要在指定的column/field上使用codec, Cluster初始化时不需要register:

Cluster cluster = Cluster.builder()
    .addContactPoint("127.0.0.1")
    .build();

MappingManager mappingManager = new MappingManager(cluster.connect());

 

@Column注解就起到作用了

@Table(name = "user")
public class User {
  @PartitionKey
  private int id;
  @Column(codec = MyCustomDateCodec.class)
  private MyCustomDate birth;

  ... // getters and setters
}

The class must have a no-arg constructor. The mapper will create an instance (one per column) and cache it for future use.

This also works with @Field and @Param annotations.

 

Implicit UDT codecs

隐式codec

@UDT(name = "address")
public class Address { ... }

@Entity(name = "user")
public class User {
  ...
  private Address address;
  ...
}

Mapper<User> userMapper = mappingManager.mapper(User.class);

// Codec is now registered for Address <-> CQL address
Row row = session.execute("select address from user where id = 1").one();
Address address = row.get("address", Address.class);

If you don't use entity mappers but still want the convenience of the UDT codec for core driver methods, the mapper provides a way to create it independently:

mappingManager.udtCodec(Address.class);

// Codec is now registered

 

 

 

 

 

 

 

 

 

分界线, 上面的是使用,下面的是介绍//

Custom codecs支持 transparent, user-configurable mapping of CQL types to arbitrary Java objects.

这种特征的实际用例很多:

  • Ability to map CQL timestamp, date and time columns to Java 8 or Joda Time classes (rather than the built-in mappings for Date, LocalDate and Long);
  • Ability to map CQL varchar columns directly to JSON or XML mapped objects (i.e. using frameworks such as Jackson);
  • Ability to map CQL user-defined types to Java objects;
  • Ability to map CQL lists directly to Java arrays;
  • Ability to map CQL collections directly to Scala collections;
  • etc.

Overview of the serialization mechanism 序列化机制概述

序列化机制的中心部分是TypeCodec。

每一个TypeCodec支持 Java type和CQL type 双向映射.  因此TypeCodec能够进行4项基本操作

  • Serialize a Java object into a CQL value;
  • Deserialize a CQL value into a Java object;
  • Format a Java object into a CQL literal;
  • Parse a CQL literal into a Java object.

 

Implementing and using custom codecs

有这样一种场景: user有JSON文档store在varchar column, 他希望driver使用Jackson library自动映射此column 为Java对象,而不是返回一个原生的JSON string.

简单的table结构如下

CREATE TABLE t (id int PRIMARY KEY, json VARCHAR);

首先实现一个合适的codec. 使用Jackson, 一个可能的Json codec 如下:

/**
 * A simple Json codec.
 */
public class JsonCodec<T> extends TypeCodec<T> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public JsonCodec(Class<T> javaType) {
        super(DataType.varchar(), javaType);
    }

    @Override
    public ByteBuffer serialize(T value, ProtocolVersion protocolVersion) throws InvalidTypeException {
        if (value == null)
            return null;
        try {
            return ByteBuffer.wrap(objectMapper.writeValueAsBytes(value));
        } catch (JsonProcessingException e) {
            throw new InvalidTypeException(e.getMessage(), e);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public T deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) throws InvalidTypeException {
        if (bytes == null)
            return null;
        try {
            byte[] b = new byte[bytes.remaining()];
            // always duplicate the ByteBuffer instance before consuming it!
            bytes.duplicate().get(b);
            return (T) objectMapper.readValue(b, toJacksonJavaType());
        } catch (IOException e) {
            throw new InvalidTypeException(e.getMessage(), e);
        }
    }

    @Override
    public String format(T value) throws InvalidTypeException {
        if (value == null)
            return "NULL";
        String json;
        try {
            json = objectMapper.writeValueAsString(value);
        } catch (IOException e) {
            throw new InvalidTypeException(e.getMessage(), e);
        }
        return '\'' + json.replace("\'", "''") + '\'';
    }

    @Override
    @SuppressWarnings("unchecked")
    public T parse(String value) throws InvalidTypeException {
        if (value == null || value.isEmpty() || value.equalsIgnoreCase("NULL"))
            return null;
        if (value.charAt(0) != '\'' || value.charAt(value.length() - 1) != '\'')
            throw new InvalidTypeException("JSON strings must be enclosed by single quotes");
        String json = value.substring(1, value.length() - 1).replace("''", "'");
        try {
            return (T) objectMapper.readValue(json, toJacksonJavaType());
        } catch (IOException e) {
            throw new InvalidTypeException(e.getMessage(), e);
        }
    }

    protected JavaType toJacksonJavaType() {
        return TypeFactory.defaultInstance().constructType(getJavaType().getType());
    }

}

 

几个实施指南:

  • Your codecs should be thread-safe, or better yet, immutable;
  • Your codecs should be fast: do not forget that codecs are executed often and are usually very "hot" pieces of code;
  • Your codecs should never block.

接下来register你的codec with CodecRegistry实例

JsonCodec<MyPojo> myJsonCodec = new JsonCodec<MyPojo>(MyPojo.class);
CodecRegistry myCodecRegistry = cluster.getConfiguration().getCodecRegistry();
myCodecRegistry.register(myJsonCodec);

Cluster's CodecRegistry 是最简单的方式, Cluster实例默认使用CodecRegistry.DEFAULT_INSTANCE,对大多数用来说已经够用.

然而,我们还是可以create一个使用不同CodecRegistry的cluster

CodecRegistry myCodecRegistry = new CodecRegistry();
Cluster cluster = new Cluster.builder().withCodecRegistry(myCodecRegistry).build();

Note:新的CodecRegistry,会自动register所有默认的codecs.

 

Cluster cluster = ...
Session session = ...
MyPojo myPojo = ...
// Using SimpleStatement
Statement stmt = new SimpleStatement("INSERT INTO t (id, json) VALUES (?, ?)", 42, myPojo));
// Using the Query Builder
BuiltStatement insertStmt = QueryBuilder.insertInto("t")
    .value("id", 42)
    .value("json", myPojo);
// Using BoundStatements
PreparedStatement ps = session.prepare("INSERT INTO t (id, json) VALUES (?, ?)");
BoundStatement bs1 = ps.bind(42, myPojo); // or alternatively...
BoundStatement bs2 = ps.bind()
    .setInt(0, 42)
    .set(1, myPojo, MyPojo.class);

 

And here is how to retrieve a MyPojo object converted from a JSON document:

ResultSet rs = session.execute(...);
Row row = rs.one();
// Let the driver convert the string for you...
MyPojo myPojo = row.get(1, MyPojo.class);
// ... or retrieve the raw string if you need it
String json = row.get(1, String.class); // row.getString(1) would have worked too

 

....

https://github.com/datastax/java-driver/tree/3.x/manual/custom_codecs

 

转载于:https://www.cnblogs.com/tianyuxieshen/p/6273935.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值