Skip to content

Achilles Annotations

DuyHai DOAN edited this page Aug 25, 2017 · 25 revisions

Below is a list of all Achilles annotations


@ASCII

This annotation tells Achilles to map a Java String field to Cassandra ascii type.

Example:

    @Table
    public class Entity 
    {
        @PartitionKey
        private Long id;
        
        @ASCII
        @Column 
        private String ascii;
        ...
        
    }    

@ClusteringColumn

This annotation indicates which field is a clustering column. Put this annotation on several fields to have many clustering columns. The attribute value indicates the ordering of this clustering column. If you want to store the data in reversed order, set the attribute asc to false The attribute asc indicates the sorting order. By default asc = true

Unlike Java indexes, the ordering starts at 1. It is a design choice since it is more natural for human being to start counting at 1

    @Table
    public class MyEntity 
    {
        @PartitionKey
        private Long userId;
        
        @ClusteringColumn(value = 1, asc = false)
        private Date date;
         
        @ClusteringColumn(2)
        private String type;
        
        ...
    }
    

@Codec

Define a custom codec system to encode any Java type into Cassandra-compatible types.

The provided Codec class should implement the Codec interface.



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;
    
    @Column
    private List<@Codec(LongToString.class) Long> listOfLongToString;
    
    @Column
    private Map<Integer, @Codec(LongToString.class) Long> mapOfLongToStringValue;
    

@CodecRegistry

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 @JSON)
        @JSON
        private AnotherBean beanToJSON;
    
        //Source type = MyEnum, target type = int (because of Encoding.ORDINAL)
        @Enumerated(Encoding.ORDINAL)
        private MyEnum enumToOrdinal;
    }    

See Codec Registry for more details


@Column

Indicates a field to be mapped by Achilles. When the value attribute of @Column is given, the field will be mapped to this name. Otherwise the field name will be used.

Example:

    @Column("age_in_years")
    private Long age;
     

Remark: the custom value set on a column will override any Naming Strategy defined on the table/globally.

The case of the column name defined with @Column will be respected by Achilles and if the column name has mixed upper & lower cases, Achilles will enclose it between double quotes when creating the table.

Example:

    @Column("itemsCount")
    private Long itemsCount;
     

The generated DDL script would be:

CREATE TABLE ...
(
    ...
    
    "itemsCount" bigint,
    
    ...
)

Similarly, all generated CQL statements will enquote the column name.


@CompileTimeConfig

You can specify configuration parameters at compie time using the CompileTimeConfig as below:

    @CompileTimeConfig(cassandraVersion = CassandraVersion.CASSANDRA_3_0_X)
    public interface AchillesConfig {
    
    }

Depending on the chosen version, Achilles will generate appropriate DSL code. You can also specify the column mapping strategy, naming strategy, insert strategy and the project name.

See Configuring Achilles at compile time for more details.


@Computed

This annotation defines a computed column e.g. a column generated by a function call. For example to define the column writetime(value):

    @Table
    public class Entity {
    
        ...
        
        @Column
        private String value;
        
        @Computed(function = "writetime", alias="writetime_value", targetColumns = {"value"}, cqlClass = Long.class)
        private Long valueWriteTime;
    }

This annotation defines 4 attributes:

  1. function: name of the CQL function to apply. It can be a pre-defined function like writetime() or an user defined function
  2. alias: alias for this column. Necessary for disambiguation in case the same function is applied on different columns
  3. targetColumns: list of CQL columns (and not Java field name) to apply the function to. If the function accepts multiple parameters, the order of the provided columns does matter
  4. cqlClass: the CQL-compatible java class that represents the output type of the function
    @Table
    public class Entity {
    
        ...
        
        @Column("first_name")
        private String firstname;
        
        @Computed(function = "writetime", alias="writetime_firstname", targetColumns = {"first_name"}, cqlClass = Long.class)
        private Long valueWriteTime;
    }

In the above example, targetColumns should point to first_name, which is the CQL column, and not firstname which represents the Java field name


@Consistency

This annotation should be used on an entity

You need to specify the read, write and serial attribute to define the corresponding consistency level.

Example:

    @Table
    @Consistency(read=ConsistencyLevel.ONE, write=ConsistencyLevel.QUORUM, serial=ConsistencyLevel.SERIAL)
    public class Entity 
    {
        ...
    
    }
    

This annotation lets Achilles generates appropriates SELECT query DSL. Please note that this annotation is only valid for Cassandra versions:

  • DSE_4_8
  • DSE_5_0_X

Important: Achilles will NOT attempt to create the index for DSE Search even if doForceSchemaCreation() is set to true.

You should create the index in DSE yourself using dsetool create core .... (please refer to DSE documentation)

Nevertheless, Achilles will check the existence of DSE Search index at runtime and will complain if it cannot be found.

Also, please note that currently OR clause is not yet supported by Achilles. Please use ...where().RawSolrQuery(String rawSolrQuery) to search using OR clauses

Additionally, you need not map the solr_query in your Java bean. Just put the @DSE_Search annotation on the fields you want to search and Achilles will generate the appropriate DSL source code.

This annotation exposes the fullTextSearchEnabled attribute which is only useful on a text/ascii field/column.

If enabled, Achilles will generate:

  • StartWith(String prefix)
  • EndWith(String suffix)
  • Contains(String substring)

methods in addition of the standard Eq(String term) and RawPredicate(String rawSolrPredicate) methods


@EmptyCollectionIfNull

For collections and maps, Cassandra does not distinguish between empty collection/map and null collection/map.

Therefore if you save an empty list, it is equivalent to setting null to this list, thus deleting it. When reading back the list, Cassandra will return a null value so Achilles will map back to a null list.

To avoid having to check for null, you can add the @EmptyCollectionIfNull annotation on a collection or map, Achilles will then map null value to an empty instance of the collection/map.


@EntityCreator

Define the custom constructor to be use to instantiate the entity. There should be maximum one non-default constructor annotated with @EntityCreator.

All the parameters of this custom constructor should:

  1. have the same name as an existing mapped field
  2. have the same Java type as an existing mapped field (for primitive types, we rely on autoboxing so you can use java.lang.Long or long for example, it does not matter)

Please note that it is not mandatory to inject all mapped-fields in your custom constructor, some fields can be set using setter or just let be null

If you wish to use different parameter name rather than sticking to the field name, you can declare manually all the field names matching the parameters using the value attribute

Example:

@Table
public class MyEntity {

    @PartitionKey
    private Long sensorId;

    @ClusteringColumn
    private Date date;

    @Column
    private Double value;

    //Correct custom constructor with matching field name and type
    @EntityCreator
    public MyEntity(Long sensorId, Date date, Double value) {
        this.sensorId = sensorId;
        this.date = date;
        this.value = value;
    }

    //Correct custom constructor with matching field name and type even if fewer parameters than existing field name
    @EntityCreator
    public MyEntity(Long sensorId, Date date) {
        this.sensorId = sensorId;
        this.date = date;
    }

    //Correct custom constructor with matching field name and autoboxed type (long)
    @EntityCreator
    public MyEntity(long sensorId, Date date, Double value) {
        this.sensorId = sensorId;
        this.date = date;
        this.value = value;
    }
    //Correct custom constructor with declared field name and type
     @EntityCreator({"sensorId", "date", "value"})
     public MyEntity(Long id, Date date, Double value) {
         this.sensorId = id;
         this.date = date;
         this.value = value;
     }

     //Incorrect custom constructor because non matching field name (myId)
     @EntityCreator
     public MyEntity(Long myId, Date date, Double value) {
         this.sensorId = myId;
         this.date = date;
         this.value = value;
     }

     //Incorrect custom constructor because field name not found (sensor_id)
     @EntityCreator({"sensor_id", "date", "value"})
     public MyEntity(Long sensor_id, Date date, Double value) {
         this.sensorId = sensor_id;
         this.date = date;
         this.value = value;
     }

     //Incorrect custom constructor because all field names are not declared (missing declaration of "value" in annotation)
     @EntityCreator({"sensorId", "date"})
     public MyEntity(Long sensor_id, Date date, Double value) {
         this.sensorId = sensor_id;
         this.date = date;
         this.value = value;
     }
}

@Enumerated

Define the encoding for enum values. There are 2 defined encoding types: Encoding.NAME and Encoding.ORDINAL, which use respectively the enum name & ordinal for encoding.

Example:

    @Column(name = "pricing")
    @Enumerated(Encoding.ORDINAL)
    private Pricing pricing;
    
    @Column(name = "pricings")
    private List<@Enumerated(Encoding.ORDINAL) Pricing> pricings;
    
    @Column(name = "pricing_per_country")
    private Map<@Enumerated(Encoding.ORDINAL) Country, @Enumerated(Encoding.ORDINAL) PricingType> pricingPerCountry;    

More details on Enum type


@Frozen

Define a frozen UDT or nested frozen UDT/collection

    @Table
    public class Entity {
    
        @Column
        @Frozen
        private MyUDT myUdt;
        
        @Column
        private List<@Frozen MyUDT> udtList;
        
        @Column
        private List<@Frozen Map<Integer, String>> nestedCollection;
    
    }

@FunctionRegistry

Marks a class as a function registry and let Achilles manage it

@FunctionRegistry
public interface MyFunctions {

    Integer sumOf(int val1, int val2);

    Long toLong(Date javaDate);
}

Note: it is possible to declare several function registries in your source code, just annotate them with @FunctionRegistry

Warning: it is not possible to declare 2 different functions with the same name and signature in the same keyspace Achilles will raise a compilation error when encountering such case. Ex:

@FunctionRegistry
public interface MyFunctionRegistry {


    String toString(long value);

    String toString(int value); // OK because parameter type is different

    String toString(long value); // KO because same signature as the first function
}

Remark 1: functions return types cannot be primitive, use boxed types instead
Remark 2: Achilles' codec system also applies for function parameters and return type

@FunctionRegistry
public interface FunctionsWithCodecSystemRegistry {

    // CQL function signature = listtojson(consistencylevels list<text>), returns text
    String listToJson(List<@Enumerated ConsistencyLevel> consistencyLevels);

    // CQL function signature = getinvalue(input text), returns text
    @Codec(IntToString.class) String getIntValue(String input);

}

Remark 3: functions name and parameters' name are lower-cased by Cassandra automatically

The @FunctionRegistry annotation accepts a keyspace attribute to specify in which keyspace the declared functions belong to. If not specified the currently logged keyspace will be used.

See Functions Mapping for more details


@Immutable

Mark an entity/udt/view as immutable. The immutable entity should comply to the following rules

  1. all fields should have public final modifiers
  2. have neither getter nor setter
  3. have exactly one non-default constructor that:
    • has as many argument as there are fields
    • each argument should have the same type as its corresponding field (for primitive types, we rely on autoboxing so you can use java.lang.Long or long for example, it does not matter)
    • each argument name should match an existing mapped field name

Example of correct mapping:

 @Table
 @Immutable
 public class MyImmutableEntity {
 
     @PartitionKey
     public final Long sensorId;
 
     @ClusteringColumn
     public final Date date;
 
     @Column
     public final Double value;
 
     //Correct non-default constructor with matching field name and type
     public MyImmutableEntity(long sensorId, Date date, Double value) {
         this.sensorId = sensorId;
         this.date = date;
         this.value = value;
     }
 
     // NO GETTER NOR SETTER !!!!!!!!!!!!!!!
 }

Example of wrong mapping because constructor argument name does not match field name:

@Table
@Immutable
public class MyImmutableEntity {

    @PartitionKey
    public final Long sensorId;

    @ClusteringColumn
    public final Date date;

    @Column
    public final Double value;

    //Incorrect, there is no field name "sensor_id" !!
    public MyImmutableEntity(long sensor_id, Date date, Double value) {
        this.sensorId = sensorId;
        this.date = date;
        this.value = value;
    }

    // NO GETTER NOR SETTER !!!!!!!!!!!!!!!
}

Example of wrong mapping because constructor argument type does not match field type:

 
@Table
@Immutable
public class MyImmutableEntity {

    @PartitionKey
    public final Long sensorId;

    @ClusteringColumn
    public final Date date;

    @Column
    public final Double value;

    //Incorrect, field sensorId is of type Long, not String !!
    public MyImmutableEntity(String sensor_id, Date date, Double value) {
        this.date = date;
        this.value = value;
    }

    // NO GETTER NOR SETTER !!!!!!!!!!!!!!!
}

@Index

This annotation defines a secondary index on a regular column

Additionally you can define the name of the index using the name attribute on the annotation.

Example

@Table
public class User {

    @PartitionKey
    private Long id;

    //Simple index
    @Column
    @Index
    private String countryCode;

    //Simple index with custom name
    @Column("custom_name")
    @Index(name = "country_code_idx")
    private String customName;

    //Index on collection
    @Column
    @Index
    private List<String> indexedList;

    //Full index on collection because of the usage of @Frozen
    @Column
    @Frozen
    @Index
    private List<String> indexedFullList;

    //Index on map key
    @Column("indexed_map_key")
    private Map<@Index String, Long> indexOnMapKey;

    //Index on map entry
    @Column("index_map_entry")
    @Index
    private Map<String, Long> indexOnMapEntry;

    //Custom index
    @Column
    @Index(indexClassName = "com.compagny.index.SecondaryIndex", indexOptions = "{'key1': 'value1', 'key2': 'value2'}")
    private String custom;
	
	...
}    

The above code will generate the following CQL script:

    CREATE INDEX IF NOT EXISTS ON my_entity(countryCode);
    
    CREATE INDEX country_code_idx IF NOT EXISTS ON my_entity(custom_name);
    
    CREATE INDEX IF NOT EXISTS ON my_entity(indexedlist);
    
    CREATE INDEX IF NOT EXISTS ON my_entity(FULL(indexedfulllist));
    
    CREATE INDEX IF NOT EXISTS ON my_entity(KEYS(indexed_map_key));
    
    CREATE INDEX IF NOT EXISTS ON my_entity(ENTRY(index_map_entry));
    
    CREATE CUSTOM my_entity_custom_index INDEX IF NOT EXISTS
    ON my_entity(custom)
    USING 'com.compagny.index.SecondaryIndex'
    WITH OPTIONS = {'key1': 'value1', 'key2': 'value2'};

The @Index annotation exposes the following attributes:

  1. name: index name. If not provided, will be "fieldname_index"
  2. indexClassName: class name of custom index. This class should be present on the Cassandra server
  3. indexOptions: index option string. Use the JSON map style {'key1': 'property1', 'key2': 'property2', ...}

@JSON

Tell Achilles to serialize an un-supported data type into JSON string. Example:

    //Simple value encoding
    @Column
    @JSON
    private MyPOJO pojoAsJson;
    
    //Collection value encoding
    @Column
    private List<@JSON MyPOJO> myPojosAsJson;
    
    //Map key encoding
    @Column
    private Map<@JSON MyPOJO, String> mapKeyEncoding;
        
    //Map value encoding
    @Column
    private Map<String, @JSON MyPOJO> mapValueEncoding;
    
    // Tuple value encoding
    @Column
    private Tuple3<String, @JSON MyPOJO, Integer> tupleValueEncoding;
 

@MaterializedView

Marks a class as a materialized view and let Achilles manage it

    @MaterializedView(baseEntity = UserEntity.class)
    public class UserByCountryView {...}
    

This annotation defines 3 attributes:

  • baseEntity (MANDATORY): the entity class from which this materialized view is derived
  • keyspace (OPTIONAL): the name of the keyspace in which this materialized view belongs to
  • view (OPTIONAL): the name of the materialized view. Defaults to the short class name if not provided

See Materialized View Mapping for more details


@PartitionKey

This annotation indicates which field is a partition key for the table Put this annotation on several fields to have a composite partition key. The attribute value indicates the ordering of this partition component in a composite partition key. The attribute value defaults to 1.

Unlike Java indexes, the ordering starts at 1. It is a design choice since it is more natural for human being to start counting at 1

	private static class Entity 
	{
		@PartitionKey
		private Long id;

		@ClusteringColumn
		private UUID date;
		...
	}
	
	private static class CompositePartitionEntity
	{
		@PartitionKey(1)
		private Long id;

		@PartitionKey(2)
		private String type;

		@ClusteringColumn
		private UUID date;
		...
	}	

In the above example, id and type are part of the composite partition key. date is the clustering key.

Remark: all fields annotated with @PartitionKey should be consecutive with respect to their ordering. Failing this condition will raise an exception during compilation


@RuntimeCodec

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:

  1. 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
  2. codecName (OPTIONAL): useful to distinguish 2 different codecs having the same sourceType and targetType

@SASI

Define a SASI index on the column and generate appropriate DSL code for SELECT query.

The annotation exposes the following attributes:

Attribute Possible values Description Default value
indexMode IndexMode.PREFIX, IndexMode.CONTAINS or IndexMode.SPARSE
  • PREFIX (DEFAULT): allows search on prefix for text/ascii data types. Default and only valid index mode for non-text data types
  • CONTAINS: allows search on prefix, suffix and substring for text/ascii data types. Invalid for non-text data types
  • SPARSE: only valid for non-text data types. SPARSE mode is optimized for low-cardinality e.g. for indexed values having 5 or less corresponding rows. If there are more than 5 CQL rows having this index value, SASI will complain by throwing an exception
IndexMode.PREFIX
analyzed true or false Indicates whether the data should be analyzed or not. Setting 'analyzed' = true is only valid for text/ascii data types. Setting 'analyzed' = true is mandatory if 'analyzerClass' is set to:
  • NON_TOKENIZING_ANALYZER
  • STANDARD_ANALYZER
false
analyzerClass Analyzer.NO_OP_ANALYZER, Analyzer.NON_TOKENIZING_ANALYZER or Analyzer.STANDARD_ANALYZER Defines the analyzer class. Available values are:
  • NO_OP_ANALYSER (DEFAULT): do not analyze the input
  • NON_TOKENIZING_ANALYZER: only valid for text/ascii data types. Do not tokenize the input. Normalization by lowercase/uppercase is allowed
  • STANDARD_ANALYZER: only valid for text/ascii data types. Split the input text into tokens, using the locale defined by attribute 'locale' Normalization by lowercase/uppercase is allowed
Please note that setting 'analyzerClass' to NON_TOKENIZING_ANALYZER or STANDARD_ANALYZER also requires setting 'analyzed' to true
Analyzer.NO_OP_ANALYZER
maxCompactionFlushMemoryInMb any integer Maximum size of SASI data to keep in memory during compaction process. If there are more than 'maxCompactionFlushMemoryInMb' worth of index data, SASI will flush them on temporary files on disk before merging all the temp files into a single one. Of course it will add up to compaction duration. No free lunch, sorry 1024 (e.g. 1Gb)
normalization Normalization.NONE, Normalization.LOWERCASE or Normalization.UPPERCASE Defines the normalization to be applied to the input. Available values are:
  • NONE (DEFAULT): no normalization
  • LOWERCASE: normalize input text and search term to lower case
  • UPPERCASE: normalize input text and search term to upper case
Normalization.NONE
locale any valid locale string Defines the locale for tokenization. This attribute is only used when 'analyzerClass' == STANDARD_ANALYZER otherwise it is ignored "en"
enableStemming true or false Enable stemming of input text. This attribute is only used when 'analyzerClass' == STANDARD_ANALYZER false
skipStopWords true or false Enable stemming of input text. This attribute is only used when 'analyzerClass' == STANDARD_ANALYZER false

The following combinations are allowed for index options:

Data type Index Mode Analyzer Class Possible option values
Text or Ascii PREFIX or CONTAINS NoOpAnalyzer
  • analyzed = false (DEFAULT)
  • normalization = NONE (DEFAULT)
  • locale is ignored
  • maxCompactionFlushMemoryInMb (OPTIONAL)
  • enableStemming = false (DEFAULT)
  • skipStopWords = false (DEFAULT)
Text or Ascii PREFIX or CONTAINS NonTokenizingAnalyzer
  • analyzed = true (MANDATORY)
  • normalization (OPTIONAL)
  • locale (OPTIONAL)
  • maxCompactionFlushMemoryInMb (OPTIONAL)
  • enableStemming = false (DEFAULT)
  • skipStopWords = false (DEFAULT)
Text or Ascii PREFIX or CONTAINS StandardAnalyzer
  • analyzed = true (MANDATORY)
  • normalization (OPTIONAL)
  • locale (OPTIONAL)
  • maxCompactionFlushMemoryInMb (OPTIONAL)
  • enableStemming (OPTIONAL)
  • skipStopWords (OPTIONAL)
Non Text PREFIX OR SPARSE NoOpAnalyzer
  • analyzed = false (DEFAULT)
  • normalization = NONE (DEFAULT)
  • locale is ignored
  • maxCompactionFlushMemoryInMb (OPTIONAL)
  • enableStemming = false (DEFAULT)
  • skipStopWords = false (DEFAULT)

@Static

Defines a static column

    @Table
    public class Entity {
    
        @PartitionKey
        private Long id;
        
        @Column
        @Static
        private String staticVal;
        
        @ClusteringColumn
        private Date date;
        
        ...
    } 

The @Static annotation can only be used in an entity having @ClusteringColumn otherwise Achilles will raise an exception


@Strategy

Define the insert and naming strategy on an entity.
For the insert strategy, 2 values are possible: info.archinnov.achilles.type.InsertStrategy.NOT_NULL_FIELDS and info.archinnov.achilles.type.InsertStrategy.ALL_FIELDS

Upon call to insert(), depending on the chosen strategy Achilles will

  1. insert all fields on the entity, even if they are null
  2. insert only non null fields of the entity
    @Table
    @Strategy(insert = InsertStrategy.ALL_FIELDS)
    public class MyBean 
    {
        @PartitionKey
        private Long id;	
        ...
    }
        

Check here for more details on the Insert strategy


For the naming strategy, 3 values are possible: `info.archinnov.achilles.type.NamingStrategy.SNAKE_CASE`, `info.archinnov.achilles.type.NamingStrategy.LOWER_CASE` and `info.archinnov.achilles.type.NamingStrategy.CASE_SENSITIVE`
  • SNAKE_CASE: transform all schema name using snake case
  • CASE_SENSITIVE: enclose the name between double quotes (") for escaping the case
  • LOWER_CASE: transform the name to lower case

If not set, the strategy defaults to info.archinnov.achilles.type.NamingStrategy.LOWER_CASE

    @Table(keyspace="myKeyspace", table="myTable")
    @Strategy(naming = NamingStrategy.SNAKE_CASE)
    // final keyspace name will be transformed to 'myKeyspace' and table name to 'myTable' because
    // they are defined statically by @Table
    public class Entity 
    {
        @PartitionKey
        private Long id;	
        
        @Column
        private String firstName;  //column name transformed to 'first_name'
        
        @Column(name = "misc")
        private String customInfo; //column name will be 'misc' because name defined on @Column will override any NamingStrategy
        ...
    }    
        

Check here for more details on the Naming strategy


@Table

Indicates that an entity is candidate for persistence. By default Achilles creates a table whose name is the short class name of the entity. If you want to define a specific keyspace name, set the keyspace attribute. If you want to define a specific table name, set the table attribute.

Example:

    @Table(keyspace = "back_end", table="users")
    public class User 
    {
        ...
        ...
    }
    

> **Please note that Cassandra limits the table name to 48 characters max (because of limitations in Windows for file path max lengths)**

@TimeUUID

This annotation tells Achilles to map a Java UUID field to Cassandra timeuuid type.

Example:

    @Table
    public class Entity 
    {
        @PartitionKey
        private Long id;
        
        @TimeUUID
        @Column 
        private UUID date;
        ...
        
    }    

This is especially useful in CQL to map to timeuuid type so you can use Timeuuid functions like dateOf()/now()/minTimeuuid()/maxTimeuuid() or unixTimestampOf() on native queries


@TTL

This annotation should be used on an entity to define the default time to live in seconds

    @Table
    @TTL(3600) //1h time to live
    public class Entity 
    {
        ...
    
    }
    

@UDT

Put on a JavaBean, this annotation indicates that this class is an UDT

    @UDT(keyspace = "my_ks", name="user_udt")
    public class UserUDT {
    
        @Column
        private String firstname;
        
        @Column
        private String lastname;
        
        ...
    }

Then you can re-use the UDT class in another entity

    @Table
    public class Tweet {
    
        @PartitionKey
        private UUID tweetId;
        
        @Column
        private String content;
        
        @Column
        @Frozen
        private UserUDT author;
    }

It is necessary to use the @Frozen annotation on the UDT field

Home

Clone this wiki locally