Cassandra 的Custom Codecs

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

Declaring codecs

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

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

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

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:github

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类, 而不须要额外的配置:json

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

  ... // getters and setters
}

 

This also works in accessors:session

@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:app

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

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

 

@Column注解就起到做用了ide

@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.ui

This also works with @Field and @Param annotations.spa

 

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

相关文章
相关标签/搜索