diff --git a/src/main/asciidoc/reference/mapping.adoc b/src/main/asciidoc/reference/mapping.adoc index d77af62b3..8359ea5dd 100644 --- a/src/main/asciidoc/reference/mapping.adoc +++ b/src/main/asciidoc/reference/mapping.adoc @@ -1,30 +1,28 @@ [[mapping.chapter]] = Mapping -Rich object mapping support is provided by the `MappingCassandraConverter`. `MappingCassandraConverter` has a -rich metadata model that provides a complete feature set of functionality to map domain objects to CQL tables. +Rich object mapping support is provided by the `MappingCassandraConverter`. `MappingCassandraConverter` has a rich metadata model that provides a complete feature set of functionality to map domain objects to CQL tables. -The mapping metadata model is populated by using annotations on your domain objects. However, the infrastructure -is not limited to using annotations as the only source of metadata. The `MappingCassandraConverter` also lets you -map domain objects to tables without providing any additional metadata, by following a set of conventions. +The mapping metadata model is populated by using annotations on your domain objects. +However, the infrastructure is not limited to using annotations as the only source of metadata. +The `MappingCassandraConverter` also lets you map domain objects to tables without providing any additional metadata, by following a set of conventions. -In this chapter, we describe the features of the `MappingCassandraConverter`, how to use conventions for -mapping domain objects to tables, and how to override those conventions with annotation-based mapping metadata. +In this chapter, we describe the features of the `MappingCassandraConverter`, how to use conventions for mapping domain objects to tables, and how to override those conventions with annotation-based mapping metadata. include::../{spring-data-commons-docs}/object-mapping.adoc[leveloffset=+1] - [[mapping-conversion]] == Data Mapping and Type Conversion This section explains how types are mapped to and from an Apache Cassandra representation. -Spring Data for Apache Cassandra supports several types that are provided by Apache Cassandra. In addition to -these types, Spring Data for Apache Cassandra provides a set of built-in converters to map additional types. -You can provide your own custom converters to adjust type conversion. See "`<>`" -for further details. The following table maps Spring Data types to Cassandra types: +Spring Data for Apache Cassandra supports several types that are provided by Apache Cassandra. +In addition to these types, Spring Data for Apache Cassandra provides a set of built-in converters to map additional types. +You can provide your own custom converters to adjust type conversion. +See "`<>`" for further details. +The following table maps Spring Data types to Cassandra types: -[cols="3,2", options="header"] +[cols="3,2",options="header"] .Type |=== | Type @@ -135,25 +133,22 @@ public enum Condition { [[mapping-conventions]] == Convention-based Mapping -`MappingCassandraConverter` uses a few conventions for mapping domain objects to CQL tables when no additional -mapping metadata is provided. The conventions are: +`MappingCassandraConverter` uses a few conventions for mapping domain objects to CQL tables when no additional mapping metadata is provided. +The conventions are: * The simple (short) Java class name is mapped to the table name by being changed to lower case. For example, `com.bigbank.SavingsAccount` maps to a table named `savingsaccount`. -* The converter uses any registered Spring `Converter` instances to override the default mapping of object properties to -tables fields. +* The converter uses any registered Spring `Converter` instances to override the default mapping of object properties to tables fields. * The properties of an object are used to convert to and from properties in the table. - [[mapping-configuration]] === Mapping Configuration -Unless explicitly configured, an instance of `MappingCassandraConverter` is created by default when creating -a `CassandraTemplate`. You can create your own instance of the `MappingCassandraConverter` to tell it -where to scan the classpath at startup for your domain classes to extract metadata and construct indexes. +Unless explicitly configured, an instance of `MappingCassandraConverter` is created by default when creating a `CassandraTemplate`. +You can create your own instance of the `MappingCassandraConverter` to tell it where to scan the classpath at startup for your domain classes to extract metadata and construct indexes. -Also, by creating your own instance, you can register Spring `Converter` instances to use for mapping specific classes -to and from the database. The following example configuration class sets up Cassandra mapping support: +Also, by creating your own instance, you can register Spring `Converter` instances to use for mapping specific classes to and from the database. +The following example configuration class sets up Cassandra mapping support: .@Configuration class to configure Cassandra mapping support ==== @@ -164,23 +159,21 @@ include::../{example-root}/SchemaConfiguration.java[tags=class] ==== `AbstractCassandraConfiguration` requires you to implement methods that define a keyspace. -`AbstractCassandraConfiguration` also has a method named `getEntityBasePackages(…)`. You can override it -to tell the converter where to scan for classes annotated with the `@Table` annotation. +`AbstractCassandraConfiguration` also has a method named `getEntityBasePackages(…)`. +You can override it to tell the converter where to scan for classes annotated with the `@Table` annotation. You can add additional converters to the `MappingCassandraConverter` by overriding the `customConversions` method. -NOTE: `AbstractCassandraConfiguration` creates a `CassandraTemplate` instance and registers it with the container -under the name of `cassandraTemplate`. - +NOTE: `AbstractCassandraConfiguration` creates a `CassandraTemplate` instance and registers it with the container under the name of `cassandraTemplate`. [[mapping.usage]] == Metadata-based Mapping -To take full advantage of the object mapping functionality inside the Spring Data for Apache Cassandra support, -you should annotate your mapped domain objects with the `@Table` annotation. Doing so lets the classpath scanner find -and pre-process your domain objects to extract the necessary metadata. Only annotated entities are used -to perform schema actions. In the worst case, a `SchemaAction.RECREATE_DROP_UNUSED` operation drops your tables -and you lose your data. The following example shows a simple domain object: +To take full advantage of the object mapping functionality inside the Spring Data for Apache Cassandra support, you should annotate your mapped domain objects with the `@Table` annotation. +Doing so lets the classpath scanner find and pre-process your domain objects to extract the necessary metadata. +Only annotated entities are used to perform schema actions. +In the worst case, a `SchemaAction.RECREATE_DROP_UNUSED` operation drops your tables and you lose your data. +The following example shows a simple domain object: .Example domain object ==== @@ -207,22 +200,21 @@ public class Person { IMPORTANT: The `@Id` annotation tells the mapper which property you want to use for the Cassandra primary key. Composite primary keys can require a slightly different data model. - [[cassandra-template.id-handling]] === Working with Primary Keys -Cassandra requires at least one partition key field for a CQL table. A table can additionally declare one or more -clustering key fields. When your CQL table has a composite primary key, you must create a `@PrimaryKeyClass` to define -the structure of the composite primary key. In this context, "`composite primary key`" means one or more partition columns -optionally combined with one or more clustering columns. +Cassandra requires at least one partition key field for a CQL table. +A table can additionally declare one or more clustering key fields. +When your CQL table has a composite primary key, you must create a `@PrimaryKeyClass` to define the structure of the composite primary key. +In this context, "`composite primary key`" means one or more partition columns optionally combined with one or more clustering columns. Primary keys can make use of any singular simple Cassandra type or mapped user-defined Type. Collection-typed primary keys are not supported. ==== Simple Primary Keys -A simple primary key consists of one partition key field within an entity class. Since it is one field only, -we safely can assume it is a partition key. +A simple primary key consists of one partition key field within an entity class. +Since it is one field only, we safely can assume it is a partition key. The following listing shows a CQL table defined in Cassandra with a primary key of `user_id`: .CQL Table defined in Cassandra @@ -261,8 +253,8 @@ public class LoginEvent { ==== Composite Keys -Composite primary keys (or compound keys) consist of more than one primary key field. That said, a composite primary key -can consist of multiple partition keys, a partition key and a clustering key, or a multitude of primary key fields. +Composite primary keys (or compound keys) consist of more than one primary key field. +That said, a composite primary key can consist of multiple partition keys, a partition key and a clustering key, or a multitude of primary key fields. Composite keys can be represented in two ways with Spring Data for Apache Cassandra: @@ -290,9 +282,10 @@ CREATE TABLE login_event( ==== Flat Composite Primary Keys -Flat composite primary keys are embedded inside the entity as flat fields. Primary key fields are annotated with -`@PrimaryKeyColumn`. Selection requires either a query to contain predicates -for the individual fields or the use of `MapId`. +Flat composite primary keys are embedded inside the entity as flat fields. +Primary key fields are annotated with +`@PrimaryKeyColumn`. +Selection requires either a query to contain predicates for the individual fields or the use of `MapId`. The following example shows a class with a flat composite primary key: .Using a flat composite primary key @@ -306,10 +299,10 @@ include::../{example-root}/LoginEvent.java[tags=class] ==== Primary Key Class A primary key class is a composite primary key class that is mapped to multiple fields or properties of the entity. -It is annotated with `@PrimaryKeyClass` and must define `equals` and `hashCode` methods. The semantics of value equality -for these methods should be consistent with the database equality for the database types to which the key is mapped. -Primary key classes can be used with repositories (as the `Id` type) and to represent an entity's identity -in a single complex object. The following example shows a composite primary key class: +It is annotated with `@PrimaryKeyClass` and must define `equals` and `hashCode` methods. +The semantics of value equality for these methods should be consistent with the database equality for the database types to which the key is mapped. +Primary key classes can be used with repositories (as the `Id` type) and to represent an entity's identity in a single complex object. +The following example shows a composite primary key class: .Composite primary key class ==== @@ -349,19 +342,27 @@ An overview of the annotations follows: * `@Table`: Applied at the class level to indicate that this class is a candidate for mapping to the database. You can specify the name of the table where the object is stored. * `@PrimaryKey`: Similar to `@Id` but lets you specify the column name. -* `@PrimaryKeyColumn`: Cassandra-specific annotation for primary key columns that lets you specify -primary key column attributes, such as for clustered or partitioned. Can be used on single and multiple attributes -to indicate either a single or a composite (compound) primary key. If used on a property within the entity, make sure to apply the `@Id` annotation as well. +* `@PrimaryKeyColumn`: Cassandra-specific annotation for primary key columns that lets you specify primary key column attributes, such as for clustered or partitioned. +Can be used on single and multiple attributes to indicate either a single or a composite (compound) primary key. +If used on a property within the entity, make sure to apply the `@Id` annotation as well. * `@PrimaryKeyClass`: Applied at the class level to indicate that this class is a compound primary key class. Must be referenced with `@PrimaryKey` in the entity class. -* `@Transient`: By default, all private fields are mapped to the row. This annotation excludes the field -* `@Value`: This annotation is part of the Spring Framework . Within the mapping framework it can be applied to constructor arguments. This lets you use a Spring Expression Language statement to transform a key's value retrieved in the database before it is used to construct a domain object. In order to reference a property of a given `Row`/`UdtValue`/`TupleValue` one has to use expressions like: `@Value("#root.getString(0)")` where `root` refers to the root of the given document. -* `@ReadOnlyProperty`: Applies at the field level to mark a property as read-only. Entity-bound insert and update statements do not include this property. -where it is applied from being stored in the database. -* `@Column`: Applied at the field level. Describes the column name as it is represented in the Cassandra table, -thus letting the name differ from the field name of the class. -* `@Indexed`: Applied at the field level. Describes the index to be created at session initialization. -* `@SASI`: Applied at the field level. Allows SASI index creation during session initialization. +* `@Transient`: By default, all private fields are mapped to the row. +This annotation excludes the field where it is applied from being stored in the database. +Transient properties cannot be used within a persistence constructor as the converter cannot materialize a value for the constructor argument. +* `@PersistenceConstructor`: Marks a given constructor -- even a package protected one -- to use when instantiating the object from the database. +Constructor arguments are mapped by name to the key values in the retrieved row. +* `@Value`: This annotation is part of the Spring Framework . Within the mapping framework it can be applied to constructor arguments. +This lets you use a Spring Expression Language statement to transform a key's value retrieved in the database before it is used to construct a domain object. +In order to reference a property of a given `Row`/`UdtValue`/`TupleValue` one has to use expressions like: `@Value("#root.getString(0)")` where `root` refers to the root of the given document. +* `@ReadOnlyProperty`: Applies at the field level to mark a property as read-only. +Entity-bound insert and update statements do not include this property. +* `@Column`: Applied at the field level. +Describes the column name as it is represented in the Cassandra table, thus letting the name differ from the field name of the class. +* `@Indexed`: Applied at the field level. +Describes the index to be created at session initialization. +* `@SASI`: Applied at the field level. +Allows SASI index creation during session initialization. * `@CassandraType`: Applied at the field level to specify a Cassandra data type. Types are derived from the property declaration by default. * `@UserDefinedType`: Applied at the type level to specify a Cassandra User-defined Data Type (UDT). @@ -369,10 +370,10 @@ Types are derived from the declaration by default. * `@Tuple`: Applied at the type level to use a type as a mapped tuple. * `@Element`: Applied at the field level to specify element or field ordinals within a mapped tuple. Types are derived from the property declaration by default. -* `@Version`: Applied at field level is used for optimistic locking and checked for modification on save operations. The initial value is `zero` which is bumped automatically on every update. +* `@Version`: Applied at field level is used for optimistic locking and checked for modification on save operations. +The initial value is `zero` which is bumped automatically on every update. -The mapping metadata infrastructure is defined in the separate, spring-data-commons project that is both -technology- and data store-agnostic. +The mapping metadata infrastructure is defined in the separate, spring-data-commons project that is both technology- and data store-agnostic. The following example shows a more complex mapping: @@ -410,15 +411,15 @@ include::../{example-root}/mapping/Coordinates.java[tags=class] [[mapping.index-creation]] ==== Index Creation -You can annotate particular entity properties with `@Indexed` or `@SASI` if you wish to create secondary indexes -on application startup. Index creation creates simple secondary indexes for scalar types, user-defined types, -and collection types. +You can annotate particular entity properties with `@Indexed` or `@SASI` if you wish to create secondary indexes on application startup. +Index creation creates simple secondary indexes for scalar types, user-defined types, and collection types. You can configure a SASI Index to apply an analyzer, such as `StandardAnalyzer` or `NonTokenizingAnalyzer` (by using `@StandardAnalyzed` and `@NonTokenizingAnalyzed`, respectively). -Map types distinguish between `ENTRY`, `KEYS`, and `VALUES` indexes. Index creation derives the index type -from the annotated element. The following example shows a number of ways to create an index: +Map types distinguish between `ENTRY`, `KEYS`, and `VALUES` indexes. +Index creation derives the index type from the annotated element. +The following example shows a number of ways to create an index: .Variants of map indexing ==== @@ -435,9 +436,12 @@ include::./converters.adoc[] [[cassandra.mapping-usage.events]] == Lifecycle Events -The Cassandra mapping framework has several built-in `org.springframework.context.ApplicationEvent` events that your application can respond to by registering special beans in the `ApplicationContext`. Being based on Spring's application context event infrastructure lets other products, such as Spring Integration, easily receive these events as they are a well known eventing mechanism in Spring-based applications. +The Cassandra mapping framework has several built-in `org.springframework.context.ApplicationEvent` events that your application can respond to by registering special beans in the `ApplicationContext`. +Being based on Spring's application context event infrastructure lets other products, such as Spring Integration, easily receive these events as they are a well known eventing mechanism in Spring-based applications. -To intercept an object before it goes into the database, you can register a subclass of `org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener` that overrides the `onBeforeSave(…)` method. When the event is dispatched, your listener is called and passed the domain object (which is a Java entity). The following example uses the `onBeforeSave` method: +To intercept an object before it goes into the database, you can register a subclass of `org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener` that overrides the `onBeforeSave(…)` method. +When the event is dispatched, your listener is called and passed the domain object (which is a Java entity). +The following example uses the `onBeforeSave` method: ==== [source,java] @@ -457,7 +461,8 @@ The `AbstractCassandraEventListener` has the following callback methods: * `onAfterLoad`: Called in the `CassandraTemplate.select(…)`, `.slice(…)`, and `.stream(…)` methods after each row is retrieved from the database. * `onAfterConvert`: Called in the `CassandraTemplate.select(…)`, `.slice(…)`, and `.stream(…)` methods after converting a row retrieved from the database to a POJO. -NOTE: Lifecycle events are emitted only for root-level types. Complex types used as properties within an aggregate root are not subject to event publication. +NOTE: Lifecycle events are emitted only for root-level types. +Complex types used as properties within an aggregate root are not subject to event publication. include::../{spring-data-commons-docs}/entity-callbacks.adoc[leveloffset=+1] include::./cassandra-entity-callbacks.adoc[leveloffset=+2]