From 66dfce1781687bf99c3c7aa1961393aea036c9f4 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 5 Jul 2022 13:36:02 +0200 Subject: [PATCH] Refine table and column name generation. We revised the table and column name generation by unifying the generation code into CqlIdentifierGenerator. We also properly distinguish between generated names that are generated and those provided by the application (i.e. through annotations). Closes #1263 --- .../BasicCassandraPersistentEntity.java | 45 +++----- .../BasicCassandraPersistentProperty.java | 37 +------ .../core/mapping/CassandraMappingContext.java | 32 +++--- .../CassandraUserTypePersistentEntity.java | 9 +- .../core/mapping/CqlIdentifierGenerator.java | 103 ++++++++++++++++++ .../core/mapping/IdentifierFactory.java | 40 ------- .../core/mapping/NamingStrategy.java | 19 +++- ...sicCassandraPersistentEntityUnitTests.java | 4 +- .../core/mapping/NamingStrategyUnitTests.java | 57 +++++++++- src/main/asciidoc/reference/mapping.adoc | 4 +- 10 files changed, 215 insertions(+), 135 deletions(-) create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CqlIdentifierGenerator.java delete mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdentifierFactory.java diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java index 82711af4d..3b06e02ff 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java @@ -15,15 +15,17 @@ */ package org.springframework.data.cassandra.core.mapping; +import java.lang.annotation.Annotation; import java.util.Comparator; import java.util.Optional; +import java.util.function.BiFunction; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.expression.BeanFactoryAccessor; import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.data.cassandra.util.SpelUtils; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.MappingException; @@ -32,7 +34,6 @@ import org.springframework.data.util.TypeInformation; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import com.datastax.oss.driver.api.core.CqlIdentifier; @@ -49,14 +50,14 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity, String> defaultNameGenerator, + @Nullable Annotation annotation) { - if (!StringUtils.hasText(value)) { - return IdentifierFactory.create(getNamingStrategy().getTableName(this), forceQuote); + if (annotation != null) { + return this.namingAccessor.generate((String) AnnotationUtils.getValue(annotation), + (Boolean) AnnotationUtils.getValue(annotation, "forceQuote"), defaultNameGenerator, this, this.spelContext); } - String name = Optional.ofNullable(this.spelContext).map(it -> SpelUtils.evaluate(value, it)).orElse(value); - - Assert.state(name != null, () -> String.format("Cannot determine default name for %s", this)); - - return IdentifierFactory.create(name, forceQuote); + return this.namingAccessor.generate(null, forceQuote != null ? forceQuote : false, defaultNameGenerator, this, + this.spelContext); } /* (non-Javadoc) @@ -186,7 +180,7 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity implements CassandraPersistentProperty, ApplicationContextAware { + private final CqlIdentifierGenerator namingAccessor = new CqlIdentifierGenerator(); + // Indicates whether this property has been explicitly instructed to force quoted column names. private Boolean forceQuote; private @Nullable CqlIdentifier columnName; - private NamingStrategy namingStrategy = NamingStrategy.INSTANCE; - private @Nullable StandardEvaluationContext spelContext; /** @@ -198,8 +195,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP return null; } - Supplier defaultName = () -> getNamingStrategy().getColumnName(this); - String overriddenName = null; boolean forceQuote = false; @@ -230,7 +225,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP } } - return createColumnName(defaultName, overriddenName, forceQuote); + return namingAccessor.generate(overriddenName, forceQuote, NamingStrategy::getColumnName, this, this.spelContext); } @Override @@ -259,21 +254,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP } } - @Nullable - private CqlIdentifier createColumnName(Supplier defaultName, @Nullable String overriddenName, - boolean forceQuote) { - - String name; - - if (StringUtils.hasText(overriddenName)) { - name = this.spelContext != null ? SpelUtils.evaluate(overriddenName, this.spelContext) : overriddenName; - } else { - name = defaultName.get(); - } - - return name != null ? IdentifierFactory.create(name, forceQuote) : null; - } - /* (non-Javadoc) * @see org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty#setColumnName(org.springframework.data.cassandra.core.cql.CqlIdentifier) */ @@ -292,14 +272,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP * @since 3.0 */ public void setNamingStrategy(NamingStrategy namingStrategy) { - - Assert.notNull(namingStrategy, "NamingStrategy must not be null"); - - this.namingStrategy = namingStrategy; - } - - NamingStrategy getNamingStrategy() { - return this.namingStrategy; + this.namingAccessor.setNamingStrategy(namingStrategy); } /* (non-Javadoc) @@ -313,7 +286,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP this.forceQuote = forceQuote; if (changed) { - setColumnName(IdentifierFactory.create(getRequiredColumnName().asInternal(), forceQuote)); + setColumnName(CqlIdentifierGenerator.createIdentifier(getRequiredColumnName().asInternal(), forceQuote)); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java index 9b1249c19..5ac443b62 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java @@ -71,7 +71,7 @@ public class CassandraMappingContext private Mapping mapping = new Mapping(); - private NamingStrategy namingStrategy = NamingStrategy.INSTANCE; + private @Nullable NamingStrategy namingStrategy; private @Deprecated @Nullable UserTypeResolver userTypeResolver; @@ -131,7 +131,8 @@ public class CassandraMappingContext String entityTableName = entityMapping.getTableName(); if (StringUtils.hasText(entityTableName)) { - entity.setTableName(IdentifierFactory.create(entityTableName, Boolean.valueOf(entityMapping.getForceQuote()))); + entity.setTableName( + CqlIdentifierGenerator.createIdentifier(entityTableName, Boolean.valueOf(entityMapping.getForceQuote()))); } processMappingOverrides(entity, entityMapping); @@ -163,7 +164,7 @@ public class CassandraMappingContext property.setForceQuote(forceQuote); if (StringUtils.hasText(mapping.getColumnName())) { - property.setColumnName(IdentifierFactory.create(mapping.getColumnName(), forceQuote)); + property.setColumnName(CqlIdentifierGenerator.createIdentifier(mapping.getColumnName(), forceQuote)); } } @@ -200,7 +201,7 @@ public class CassandraMappingContext /** * @deprecated since 3.0. Use custom conversion through - * {@link org.springframework.data.cassandra.core.convert.MappingCassandraConverter}. + * {@link org.springframework.data.cassandra.core.convert.MappingCassandraConverter}. */ @Deprecated public CustomConversions getCustomConversions() { @@ -255,7 +256,7 @@ public class CassandraMappingContext /** * @deprecated since 3.0. Retrieve {@link CodecRegistry} directly from - * {@link org.springframework.data.cassandra.core.convert.CassandraConverter}. + * {@link org.springframework.data.cassandra.core.convert.CassandraConverter}. */ @Deprecated public CodecRegistry getCodecRegistry() { @@ -304,7 +305,7 @@ public class CassandraMappingContext /** * @deprecated since 3.0. Retrieve {@link UserTypeResolver} directly from - * {@link org.springframework.data.cassandra.core.convert.CassandraConverter}. + * {@link org.springframework.data.cassandra.core.convert.CassandraConverter}. */ @Nullable @Deprecated @@ -352,7 +353,6 @@ public class CassandraMappingContext if (!entity.isUserDefinedType() && !entity.isTupleType() && entity.isAnnotationPresent(Table.class)) { this.tableEntities.add(entity); } - }); return optional; @@ -375,11 +375,12 @@ public class CassandraMappingContext BasicCassandraPersistentEntity entity = isUserDefinedType(typeInformation) ? new CassandraUserTypePersistentEntity<>(typeInformation, getVerifier()) - : isTuple(typeInformation) - ? new BasicCassandraPersistentTupleEntity<>(typeInformation) - : new BasicCassandraPersistentEntity<>(typeInformation, getVerifier()); + : isTuple(typeInformation) ? new BasicCassandraPersistentTupleEntity<>(typeInformation) + : new BasicCassandraPersistentEntity<>(typeInformation, getVerifier()); - entity.setNamingStrategy(this.namingStrategy); + if (this.namingStrategy != null) { + entity.setNamingStrategy(this.namingStrategy); + } Optional.ofNullable(this.applicationContext).ifPresent(entity::setApplicationContext); return entity; @@ -404,7 +405,10 @@ public class CassandraMappingContext ? new BasicCassandraPersistentTupleProperty(property, owner, simpleTypeHolder) : new CachingCassandraPersistentProperty(property, owner, simpleTypeHolder); - persistentProperty.setNamingStrategy(this.namingStrategy); + if (this.namingStrategy != null) { + persistentProperty.setNamingStrategy(this.namingStrategy); + } + Optional.ofNullable(this.applicationContext).ifPresent(persistentProperty::setApplicationContext); return persistentProperty; @@ -445,9 +449,7 @@ public class CassandraMappingContext return getPersistentEntities().stream().flatMap(entity -> StreamSupport.stream(entity.spliterator(), false)) .flatMap(it -> Optionals.toStream(Optional.ofNullable(it.findAnnotation(CassandraType.class)))) - .map(CassandraType::userTypeName) - .filter(StringUtils::hasText) - .map(CqlIdentifier::fromCql) + .map(CassandraType::userTypeName).filter(StringUtils::hasText).map(CqlIdentifier::fromCql) .anyMatch(identifier::equals); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java index 6f8694803..4fba53b30 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java @@ -46,14 +46,7 @@ public class CassandraUserTypePersistentEntity extends BasicCassandraPersiste */ @Override protected CqlIdentifier determineTableName() { - - UserDefinedType annotation = findAnnotation(UserDefinedType.class); - - if (annotation != null) { - return determineName(annotation.value(), annotation.forceQuote()); - } - - return IdentifierFactory.create(getNamingStrategy().getUserDefinedTypeName(this), false); + return determineTableName(NamingStrategy::getUserDefinedTypeName, findAnnotation(UserDefinedType.class)); } /* (non-Javadoc) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CqlIdentifierGenerator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CqlIdentifierGenerator.java new file mode 100644 index 000000000..afd2e2897 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CqlIdentifierGenerator.java @@ -0,0 +1,103 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.core.mapping; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.springframework.data.cassandra.util.SpelUtils; +import org.springframework.expression.EvaluationContext; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.internal.core.util.Strings; + +/** + * Strategy class to generate {@link CqlIdentifier identifier names} using {@link NamingStrategy} and contextual details + * from entities and properties. + * + * @author Mark Paluch + * @since 3.4.2 + */ +class CqlIdentifierGenerator { + + private @Nullable NamingStrategy namingStrategy; + + static CqlIdentifier createIdentifier(String simpleName, boolean forceQuote) { + + if (Strings.isDoubleQuoted(simpleName)) { + return CqlIdentifier.fromCql(simpleName); + } + + if (forceQuote || Strings.needsDoubleQuotes(simpleName)) { + return CqlIdentifier.fromInternal(simpleName); + } + + return CqlIdentifier.fromCql(simpleName); + } + + /** + * Generate a {@link CqlIdentifier name} using the provided name or fall back to the default {@link Function name + * generator} using a {@link NamingStrategy}. + * + * @param providedName the name to use if provided. + * @param forceQuote whether to enforce quoting. + * @param defaultNameGenerator the default name generator. + * @param source source to be used for name generation. + * @param spelContext the SpEL evaluation context for evaluating SpEL expressions provided through + * {@code providedName}. + * @return the generated name. + */ + public CqlIdentifier generate(@Nullable String providedName, boolean forceQuote, + BiFunction defaultNameGenerator, T source, @Nullable EvaluationContext spelContext) { + + String name; + boolean useForceQuote = forceQuote; + + if (StringUtils.hasText(providedName)) { + name = spelContext != null ? SpelUtils.evaluate(providedName, spelContext) : providedName; + useForceQuote = true; + } else { + name = defaultNameGenerator.apply(getNamingStrategy(forceQuote), source); + } + + Assert.state(name != null, () -> String.format("Cannot determine default name for %s", source)); + + return createIdentifier(name, useForceQuote); + } + + public void setNamingStrategy(@Nullable NamingStrategy namingStrategy) { + + Assert.notNull(namingStrategy, "NamingStrategy must not be null"); + + this.namingStrategy = namingStrategy; + } + + private NamingStrategy getNamingStrategy(boolean forceQuote) { + + if (namingStrategy == null) { + if (forceQuote) { + return new NamingStrategy() {}; + } else { + return NamingStrategy.INSTANCE; + } + } + + return namingStrategy; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdentifierFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdentifierFactory.java deleted file mode 100644 index 6ae734237..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdentifierFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.cassandra.core.mapping; - -import com.datastax.oss.driver.api.core.CqlIdentifier; -import com.datastax.oss.driver.internal.core.util.Strings; - -/** - * Factory for {@link CqlIdentifier}. - * - * @author Mark Paluch - */ -class IdentifierFactory { - - static CqlIdentifier create(String simpleName, boolean forceQuote) { - - if (forceQuote) { - return CqlIdentifier.fromCql("\"" + simpleName + "\""); - } - - if (Strings.needsDoubleQuotes(simpleName.toLowerCase())) { - return CqlIdentifier.fromCql("\"" + simpleName.toLowerCase() + "\""); - } - - return CqlIdentifier.fromInternal(simpleName.toLowerCase()); - } -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/NamingStrategy.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/NamingStrategy.java index d5e79aa09..c51ef8ebb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/NamingStrategy.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/NamingStrategy.java @@ -15,13 +15,15 @@ */ package org.springframework.data.cassandra.core.mapping; +import java.util.Locale; import java.util.function.UnaryOperator; import org.springframework.util.Assert; /** * Interface and default implementation of a naming strategy. Defaults to table name based on {@link Class} and column - * name based on property names. + * name based on property names. Names are used as-is without quoting. Lower-case, non-keyword names are used without + * quoting. Upper-case, keyword or other names requiring quoting are used with quotes. *

* NOTE: Can also be used as an adapter. Create a lambda or an anonymous subclass and override any settings to implement * a different strategy on the fly. @@ -35,8 +37,15 @@ public interface NamingStrategy { * Empty implementation of the interface utilizing only the default implementation. *

* Using this avoids creating essentially the same class over and over again. + * + * @since 3.3.6 */ - NamingStrategy INSTANCE = new NamingStrategy() {}; + NamingStrategy CASE_SENSITIVE = new NamingStrategy() {}; + + /** + * Default implementation converting all names to {@link String#toLowerCase()}. + */ + NamingStrategy INSTANCE = CASE_SENSITIVE.transform(s -> s.toLowerCase(Locale.ROOT)); /** * Naming strategy that renders CamelCase name parts to {@code snake_case}. @@ -74,10 +83,8 @@ public interface NamingStrategy { } /** - * Apply a {@link UnaryOperator transformation function} to create a new {@link NamingStrategy} - * that applies the given transformation to each name component. - * - * Example: + * Apply a {@link UnaryOperator transformation function} to create a new {@link NamingStrategy} that applies the given + * transformation to each name component. Example: *

* NamingStrategy lower = NamingStrategy.INSTANCE.transform(String::toLowerCase); *

diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntityUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntityUnitTests.java index ae524a1f3..034f86713 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntityUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntityUnitTests.java @@ -52,12 +52,12 @@ class BasicCassandraPersistentEntityUnitTests { @Mock ApplicationContext context; @Test - void subclassInheritsAtDocumentAnnotation() { + void subclassInheritsAtTableAnnotation() { BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity<>( ClassTypeInformation.from(Notification.class)); - assertThat(entity.getTableName()).hasToString("messages"); + assertThat(entity.getTableName().asCql(true)).isEqualTo("messages"); } @Test diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/NamingStrategyUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/NamingStrategyUnitTests.java index a74fe62be..b47932ed5 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/NamingStrategyUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/NamingStrategyUnitTests.java @@ -21,6 +21,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.util.StringUtils; import com.datastax.oss.driver.api.core.CqlIdentifier; @@ -38,7 +40,48 @@ class NamingStrategyUnitTests { context.setUserTypeResolver(typeName -> { throw new IllegalStateException(""); }); - context.setNamingStrategy(NamingStrategy.INSTANCE); + } + + @Test + void getTableNameGeneratesTableName() { + + BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity<>( + ClassTypeInformation.from(TableNameHolderThingy.class)); + + assertThat(entity.getTableName().asCql(true)).isEqualTo("tablenameholderthingy"); + } + + @Test + void getTableNameGeneratesQuotedTableName() { + + BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity<>( + ClassTypeInformation.from(TableNameHolderThingy.class)); + entity.setNamingStrategy(NamingStrategy.SNAKE_CASE.transform(it -> "\"" + StringUtils.capitalize(it) + "\"")); + + assertThat(entity.getTableName().asCql(true)).isEqualTo("\"Table_name_holder_thingy\""); + + entity = new BasicCassandraPersistentEntity<>(ClassTypeInformation.from(TableNameHolderThingy.class)); + entity.setNamingStrategy(NamingStrategy.SNAKE_CASE.transform(String::toUpperCase)); + + assertThat(entity.getTableName().asCql(true)).isEqualTo("\"TABLE_NAME_HOLDER_THINGY\""); + } + + @Test + void atTableIsCaseSensitive() { + + BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity<>( + ClassTypeInformation.from(ProvidedTableName.class)); + + assertThat(entity.getTableName().asCql(true)).isEqualTo("\"iAmProvided\""); + } + + @Test + void atTableWithQuotedNameShouldRetainQuotes() { + + BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity<>( + ClassTypeInformation.from(QuotedTableName.class)); + + assertThat(entity.getTableName().asCql(true)).isEqualTo("\"IAmQuoted\""); } @Test // DATACASS-84 @@ -105,6 +148,17 @@ class NamingStrategyUnitTests { @Id String firstName; } + @Table("messages") + static class Message {} + + static class TableNameHolderThingy {} + + @Table("iAmProvided") + private static class ProvidedTableName {} + + @Table("\"IAmQuoted\"") + private static class QuotedTableName {} + @UserDefinedType private static class MyUserType { @@ -122,4 +176,5 @@ class NamingStrategyUnitTests { String firstName; } + } diff --git a/src/main/asciidoc/reference/mapping.adoc b/src/main/asciidoc/reference/mapping.adoc index 2f41f3dd7..7394ecae8 100644 --- a/src/main/asciidoc/reference/mapping.adoc +++ b/src/main/asciidoc/reference/mapping.adoc @@ -138,8 +138,8 @@ 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 properties of an object are used to convert to and from properties in the table. +* The converter uses any registered Spring `Converter` instances to override the default mapping of object properties to tables columns. +* The properties of an object are used to convert to and from columns in the table. You can adjust conventions by configuring a `NamingStrategy` on `CassandraMappingContext`. Naming strategy objects implement the convention by which a table, column or user-defined type is derived from an entity class and from an actual property.