DATACASS-741 - Polishing.

Reflect impact of transient property usage in persistence constructor.
This commit is contained in:
Mark Paluch
2020-03-19 15:16:12 +01:00
parent 54f2d278a4
commit f2b806052c

View File

@@ -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 "`<<cassandra.mapping.explicit-converters>>`"
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 "`<<cassandra.mapping.explicit-converters>>`" 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]