Additionally, it implements inspection methods that verify if the codec is suitable for a specific
CQL type and/or Java object.

TypeCodec can be freely subclassed by users willing to implement their own serialization logic.
However, users are only required to do so when they want to extend
the driver’s default set of codecs by registering additional codecs that handle
Java types that the driver is otherwise unable to process. For all other users,
the built-in set of codecs that is bundled with the driver provides
exactly the same functionality that the driver used to offer so far,
with out-of-the-box conversion from all CQL types to standard Java equivalents.

TypeCodecs are centralized in a CodecRegistry instance.
Whenever the driver needs to perform one of the aforementioned operations,
it will lookup for an appropriate codec in a CodecRegistry.
CodecRegistry is also used to manually register user-created codecs (see below for detailed instructions).
It also caches codec lookup results for faster execution whenever possible.

Implementing and using custom codecs

Let’s consider the following scenario: a user has JSON documents stored in a varchar column, and wants
the driver to automatically map that column to a Java object using the Jackson library,
instead of returning the raw JSON string.

The (admittedly simplistic) table structure is as follows:

CREATETABLEt(idintPRIMARYKEY,jsonVARCHAR);

The first step is to implement a suitable codec. Using Jackson, here is what a Json codec could look like:

/**
* A simple Json codec.
*/publicclassJsonCodec<T>extendsTypeCodec<T>{privatefinalObjectMapperobjectMapper=newObjectMapper();publicJsonCodec(Class<T>javaType){super(DataType.varchar(),javaType);}@OverridepublicByteBufferserialize(Tvalue,ProtocolVersionprotocolVersion)throwsInvalidTypeException{if(value==null)returnnull;try{returnByteBuffer.wrap(objectMapper.writeValueAsBytes(value));}catch(JsonProcessingExceptione){thrownewInvalidTypeException(e.getMessage(),e);}}@Override@SuppressWarnings("unchecked")publicTdeserialize(ByteBufferbytes,ProtocolVersionprotocolVersion)throwsInvalidTypeException{if(bytes==null)returnnull;try{byte[]b=newbyte[bytes.remaining()];// always duplicate the ByteBuffer instance before consuming it!bytes.duplicate().get(b);return(T)objectMapper.readValue(b,toJacksonJavaType());}catch(IOExceptione){thrownewInvalidTypeException(e.getMessage(),e);}}@OverridepublicStringformat(Tvalue)throwsInvalidTypeException{if(value==null)return"NULL";Stringjson;try{json=objectMapper.writeValueAsString(value);}catch(IOExceptione){thrownewInvalidTypeException(e.getMessage(),e);}return'\''+json.replace("\'","''")+'\'';}@Override@SuppressWarnings("unchecked")publicTparse(Stringvalue)throwsInvalidTypeException{if(value==null||value.isEmpty()||value.equalsIgnoreCase("NULL"))returnnull;if(value.charAt(0)!='\''||value.charAt(value.length()-1)!='\'')thrownewInvalidTypeException("JSON strings must be enclosed by single quotes");Stringjson=value.substring(1,value.length()-1).replace("''","'");try{return(T)objectMapper.readValue(json,toJacksonJavaType());}catch(IOExceptione){thrownewInvalidTypeException(e.getMessage(),e);}}protectedJavaTypetoJacksonJavaType(){returnTypeFactory.defaultInstance().constructType(getJavaType().getType());}}

A few implementation guidelines:

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.

The second step is to register your codec with a CodecRegistry instance:

Note: when you instantiate a new CodecRegistry, it automatically registers all the default codecs used by the driver.
This ensures that the new registry will not lack of an essential codec. You cannot deregister nor override
an already-registered codec, only register new ones. If you try to register a codec that would override
an existing one, the driver will log a warning and ignore it.

From now on, your custom codec is fully operational. It will be used every time the driver encounters
a MyPojo instance when executing queries, or when you ask it to retrieve a MyPojo instance from a ResultSet.

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

ResultSetrs=session.execute(...);Rowrow=rs.one();// Let the driver convert the string for you...MyPojomyPojo=row.get(1,MyPojo.class);// ... or retrieve the raw string if you need itStringjson=row.get(1,String.class);// row.getString(1) would have worked too

Tip: Row and BoundStatement have several methods to set and retrieve values.
But if you plan to use custom codecs,
make sure that you use preferably the get() and set() methods: they
avoid any ambiguity by requiring the user to explicitly specify the desired Java type,
thus forcing the driver to pick the right codec for the right task.

On-the-fly codec generation

CodecRegistry instances not only store default codecs and user-defined codecs;
they can also create new codecs on the fly, based on the set of codecs they currently hold.
They can manage the following mappings:

Collections (list, sets and maps) of known types. For example, if you registered a TypeCodec<A>, you get List<A>> handled for free. This works recursively for nested collections;

UserType instances are automatically mapped to UDTValue objects. All registered codecs are available recursively to the UDT’s fields;

TupleType instances are automatically mapped to TupleValue objects (with the same rules for nested fields);

This way, the user does not have to manually register all derived codecs for a given “base” codec.
However, other combinations of Java and CQL types not listed above cannot have their codecs created on the fly;
such codecs must be manually registered.

If the codec registry encounters a mapping that it can’t handle automatically, a CodecNotFoundException
is thrown.

Thanks to on-the-fly codec generation, it is possible to leverage the JsonCodec from our previous
example to retrieve lists of MyPojo objects stored in a CQL column of type list<text>, without
the need to explicitly declare a codec for this specific CQL type:

JsonCodec<MyPojo>myJsonCodec=newJsonCodec<MyPojo>(MyPojo.class);myCodecRegistry.register(myJsonCodec);Rowrow=...List<MyPojo>myPojos=row.getList("jsonList",MyPojo.class);// works out of the box!

First of all, create a codec that knows how to serialize this class; one possible solution
(but not necessarily the most efficient one) would be to convert Address instances into UDTValue
instances, then serialize them with another codec:

Now (and only if you intend to use your own CodecRegistry),
create it and register it on your Cluster instance:

// only if you do not intend to use CodecRegistry.DEFAULT_INSTANCECodecRegistrycodecRegistry=newCodecRegistry();Clustercluster=Cluster.builder().addContactPoints(...).withCodecRegistry(codecRegistry).build();

ResultSetrows=session.execute(...);Rowrow=rows.one();// let the driver convert it for you...Addressaddress=row.get("address",Address.class);// ...or access the low-level UDTValue, if you needUDTValueaddressUdt=row.get("address",UDTValue.class);// row.getUDTValue("address") would have worked too

Note that this solution creates an intermediary UDTValue object when
serializing and deserializing. It’s possible to bypass this step with a
lower-level implementation that manipulates the binary stream directly.
That’s also how the object mapper handles UDTs, and you can rely on the
mapper to generate UDT codecs for you; see
this page for more
information.

Support for generic (parameterized) types

Java generic (parameterized) types are fully supported, through Guava’s TypeToken API.
Be sure to only use get() and set() methods that take a TypeToken argument:

// this worksFoo<Bar>foo=row.get(0,newTypeToken<Foo<Bar>>(){})// this does not work due to type erasureFoo<Bar>foo=row.get(0,Foo.class);

Support for subtype polymorphism

Suppose the following class hierarchy:

classAnimal{}classCatextendsAnimal{}

By default, a codec will accept to serialize any object that extends or
implements its declared Java type: a codec such as
AnimalCodec extends TypeCodec<Animal> will accept Cat instances as well.

This allows a codec to handle interfaces and superclasses
in a generic way, regardless of the actual implementation being
used by client code; for example, the driver has a built-in codec
that handles List instances, and this codec is capable of
serializing any concrete List implementation.

But this has one caveat: when setting or retrieving values
with get() and set(), it is vital to pass the exact
Java type the codec handles:

Performance considerations

When the cache can be used and the registry looks up a codec, the following rules apply:

if a result was previously cached for that mapping, it is returned;

otherwise, the registry checks the list of “basic” codecs: the default ones,
and the ones that were explicitly registered (in the order that they were registered).
It calls each codec’s accepts methods to determine if it can handle the mapping,
and if so, adds it to the cache and returns it;

otherwise, the registry tries to generate a codec on the fly, according to the rules outlined above;

if the creation succeeds, the registry adds the created codec to the cache and returns it;

DataStax, Titan, and TitanDB are registered trademark of DataStax, Inc.
and its subsidiaries in the United States and/or other countries.

Apache Cassandra, Apache, Tomcat, Lucene, Solr, Hadoop, Spark, TinkerPop,
and Cassandra are trademarks of the Apache
Software Foundation or its subsidiaries in Canada, the United States
and/or other countries