-
Notifications
You must be signed in to change notification settings - Fork 92
Codec System
Sometimes it is useful to be able to transform a Java type into a native Java type supported by Cassandra.
An example could be to convert Joda DateTime
into java.util.Date
.
To support such usage, Achilles lets you define your own transformation using @Codec on each field of your entity.
The definition of the @Codec
annotation is:
public @interface Codec {
/**
* Codec Implementation class. The provided Codec class should implement the {@link info.archinnov.achilles.type.codec.Codec} interface.
*/
Class<? extends info.archinnov.achilles.type.codec.Codec> value();
}
To define a type transformer, you need to provide a codec class. Your codec class should implement the Codec interface.
Example:
Let's consider the following codec transforming a Long to a String
public class LongToString implements Codec<Long,String> {
@Override
public Class<Long> sourceType() {
return Long.class;
}
@Override
public Class<String> targetType() {
return String.class;
}
@Override
public String encode(Long fromJava) throws AchillesTranscodingException {
return fromJava.toString();
}
@Override
public Long decode(String fromCassandra) throws AchillesTranscodingException {
return Long.parseLong(fromCassandra);
}
}
Example of simple Long type to String type transformation
@Column
@Codec(LongToString.class)
private Long longToString;
Example of List<Long> to List<String> transformation
@Column
private List<@Codec(LongToString.class) Long> listOfLong;
Example of Set<Long> to Set<String> transformation
@Column
private Set<@Codec(LongToString.class) Long> setOfLong;
Example of key Map transformation: Map<Long,Double> to Map<String,Double>
@Column
private Map<@Codec(LongToString.class) Long, Double> mapKeyTransformation;
Example of value Map transformation: Map<Integer,Long> to Map<Integer,String>
@Column
private Map<Integer,@Codec(LongToString.class) Long> mapValueTransformation;
You can also set the @Codec
annotation on the target class. Example:
// Codec definition
public class BeanToStringCodec implements Codec<MyBean, String> {
@Override
public Class<MyBean> sourceType() { return MyBean.class };
@Override
public Class<String> targetType() { return String.class };
@Override
public String encode(MyBean fromJava) throws AchillesTranscodingException {...};
@Override
public MyBean decode(String fromCassandra) throws AchillesTranscodingException {...};
}
// Custom bean with @Codec annotation
@Codec(BeanToStringCodec.class)
public class MyBean {
...
}
//Usage
@Table
public class MyEntity {
...
@Column
private MyBean myBean; //No need to add @Codec because already defined on class MyBean
}
There are 2 ways to declare your codec:
- directly on-site using the
@Codec
annotation put on the field, as shown above - inside a
class
,abstract class
orinterface
annotated by@CodecRegistry
If a codec is both declared in a @CodecRegistry
and on-site. The on-site definition will have higher priority. Example:
@CodecRegistry
public interface MyCodecRegistry {
@Enumerated(Encoding.NAME)
MyEnum myEnum;
}
@Table
public class MyEntity {
@Enumerated(Encoding.ORDINAL) // has higher priority than @Enumerated(Encoding.NAME) in MyCodecRegistry
@Column
private MyEnum myEnum;
}
There are 2 types of codec:
- simple codecs using the
@Codec
as shown above - runtime codecs which are instantiated and injected into Achilles at runtime
Marker annotation to be used on configuration class for compile-time code generation. The type (class
, abstract class
or interface
) having this annotation will expose a list of codecs to be used by Achilles during source code parsing.
Ex:
@CodecRegistry
public [class | abstract class | interface] MyCodecRegistry {
//Source type = int, target type = String (according to IntToStringCodec codec)
@Codec(IntToStringCodec.class)
private int intToString;
//Source type = MyOwnType, target type = String (according to MyOwnTypeToStringCodec codec)
@Codec(MyOwnTypeToStringCodec.class)
private MyOwnType myOwnTypeToString;
//Source type = AnotherBean, target type = String (because of {@literal @}JSON)
@JSON
private AnotherBean beanToJSON;
//Source type = MyEnum, target type = int (because of Encoding.ORDINAL)
@Enumerated(Encoding.ORDINAL)
private MyEnum enumToOrdinal;
}
It is possible to declare several codec registries in your source code, just annotate them with @CodecRegistry
Warning: it is not possible to declare 2 different codecs for the same source type for all registered codec registries. Achilles will raise a compilation error when encountering such case
Ex:
@CodecRegistry
public class MyCodecRegistry {
@Codec(MyOwnTypeToStringCodec.class)
private MyOwnType myOwnTypeToString;
// ERROR, not possible to have a 2nd codec for the same source type MyOwnType
@Codec(MyOwnTypeToBytesCodec.class)
private MyOwnType myOwnTypeToBytes;
}
Transform a custom Java type into one of native types supported by the Java driver. Normally you'll use the @Codec
annotation and provide a codec class but if your codec class is stateful or its construction needs some external
dependencies and cannot be instantiated using the default no-args constructor, you can register the codec using this annotation and build it at runtime before injecting it into Achilles
Ex:
//Compile time
@Column
@RuntimeCodec(cqlClass = String.class)
private MyBean bean;
//Runtime
final Cluster cluster = .... // Create Java driver cluster object
final Codec<MyBean, String> statefulCodec = new .... // Create your codec with initialization logic here
final CodecSignature<MyBean, String> codecSignature = new CodecSignature(MyBean.class, String.class);
ManagerFactory factory = ManagerFactoryBuilder
.builder(cluster)
...
.withRuntimeCodec(codecSignature, codec)
.build();
A codec is looked up and identified uniquely at runtime using the following information:
- sourceType
- targetType (see cqlClass below)
- optionally, codecName (see below) if provided
The @RuntimeCodec
annotation has 2 attributes:
- cqlClass (MANDATORY): specify the target CQL type for the runtime codec. It is necessary to provide this type at compile time so that Achilles can generate appropriate meta data
- codecName (OPTIONAL): useful to distinguish 2 different codecs having the same sourceType and targetType
-
Bootstraping Achilles at runtime
- Runtime Configuration Parameters
-
Manager
-
Consistency Level
-
Cassandra Options at runtime
-
Lightweight Transaction (LWT)
-
JSON Serialization
-
Interceptors
-
Bean Validation (JSR-303)