DATACASS-465 - Polishing.

Remove FrozenIndicator from public API. Refactor to recursive type to reflect property type declaration nature. Rename to FrozenIndicator. Update documentation.

Original pull request: #172.
This commit is contained in:
Mark Paluch
2020-04-06 09:07:23 +02:00
parent 17adf20a14
commit b2031724ff
9 changed files with 109 additions and 122 deletions

View File

@@ -88,8 +88,9 @@ public interface ColumnType {
* @return
*/
static CassandraColumnType listOf(CassandraColumnType componentType) {
return listOf(componentType, FrozenInfo.NOT_FROZEN);
return listOf(componentType, false);
}
/**
* Creates a List {@link ColumnType} given its {@link CassandraColumnType component type}.
*
@@ -97,9 +98,9 @@ public interface ColumnType {
* @param frozen
* @return
*/
static CassandraColumnType listOf(CassandraColumnType componentType, FrozenInfo frozen) {
static CassandraColumnType listOf(CassandraColumnType componentType, boolean frozen) {
return new DefaultCassandraColumnType(ClassTypeInformation.LIST,
() -> DataTypes.listOf(componentType.getDataType(), frozen.self), componentType);
() -> DataTypes.listOf(componentType.getDataType(), frozen), componentType);
}
/**
@@ -124,18 +125,19 @@ public interface ColumnType {
* @return
*/
static CassandraColumnType setOf(CassandraColumnType componentType) {
return setOf(componentType, FrozenInfo.NOT_FROZEN);
return setOf(componentType, false);
}
/**
* Creates a Set {@link ColumnType} given its {@link CassandraColumnType component type}.
*
* @param componentType must not be {@literal null}.
* @param frozen must not be {@literal null}.
* @param frozen
* @return
*/
static CassandraColumnType setOf(CassandraColumnType componentType, FrozenInfo frozen) {
return new DefaultCassandraColumnType(ClassTypeInformation.SET, () -> DataTypes.setOf(componentType.getDataType(),frozen.self),
componentType);
static CassandraColumnType setOf(CassandraColumnType componentType, boolean frozen) {
return new DefaultCassandraColumnType(ClassTypeInformation.SET,
() -> DataTypes.setOf(componentType.getDataType(), frozen), componentType);
}
/**

View File

@@ -17,7 +17,6 @@ package org.springframework.data.cassandra.core.convert;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.cassandra.core.mapping.Frozen;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -63,21 +62,6 @@ public interface ColumnTypeResolver {
*/
CassandraColumnType resolve(TypeInformation<?> typeInformation);
/**
* Resolve a {@link CassandraColumnType} from {@link TypeInformation}. Considers potentially registered custom
* converters and simple type rules.
*
* @param typeInformation must not be {@literal null}.
* @param frozen must not be {@literal null}.
* @return
* @see org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder
* @see CassandraCustomConversions
* @throws org.springframework.dao.InvalidDataAccessApiUsageException
*/
default CassandraColumnType resolve(TypeInformation<?> typeInformation, FrozenInfo frozen) {
return resolve(typeInformation, FrozenInfo.NOT_FROZEN);
}
/**
* Resolve a {@link CassandraColumnType} from a {@link CassandraType} annotation.
*

View File

@@ -27,7 +27,8 @@ import java.util.stream.StreamSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
@@ -98,8 +99,6 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
Assert.notNull(property, "Property must not be null");
FrozenInfo frozen = getFrozenInfo(property);
if (property.isAnnotationPresent(CassandraType.class)) {
CassandraType annotation = property.getRequiredAnnotation(CassandraType.class);
@@ -128,48 +127,51 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
}
TypeInformation<?> typeInformation = property.getTypeInformation();
return resolve(typeInformation, frozen);
return resolve(typeInformation, getFrozenInfo(property));
}
private FrozenInfo getFrozenInfo(CassandraPersistentProperty property) {
private FrozenIndicator getFrozenInfo(CassandraPersistentProperty property) {
FrozenInfo frozen = FrozenInfo.frozen(property.isAnnotationPresent(Frozen.class));
AnnotatedType annotatedType = property.findAnnotatedType(Frozen.class);
if (annotatedType == null) {
return FrozenIndicator.NOT_FROZEN;
}
return getFrozenIndicator(annotatedType);
}
private FrozenIndicator getFrozenIndicator(AnnotatedType annotatedType) {
FrozenIndicator frozen = FrozenIndicator.frozen(isFrozen(annotatedType));
if (annotatedType instanceof AnnotatedParameterizedType) {
AnnotatedParameterizedType apt = (AnnotatedParameterizedType) annotatedType;
AnnotatedType[] annotatedTypes = apt.getAnnotatedActualTypeArguments();
if (annotatedTypes.length > 0) {
AnnotatedType type1 = annotatedTypes[0];
frozen = frozen.withFirstTypeParameter(type1.isAnnotationPresent(Frozen.class));
if (annotatedTypes.length > 1) {
AnnotatedType type2 = annotatedTypes[1];
frozen = frozen.withSecondTypeParameter(type2.isAnnotationPresent(Frozen.class));
}
for (AnnotatedType type : annotatedTypes) {
frozen.addNested(getFrozenIndicator(type));
}
}
return frozen;
}
private boolean isFrozen(AnnotatedType type) {
return AnnotatedElementUtils.hasAnnotation(type, Frozen.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.ColumnTypeResolver#resolve(org.springframework.data.util.TypeInformation)
*/
@Override
public CassandraColumnType resolve(TypeInformation<?> typeInformation) {
return resolve(typeInformation, FrozenInfo.NOT_FROZEN);
return resolve(typeInformation, FrozenIndicator.NOT_FROZEN);
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.ColumnTypeResolver#resolve(org.springframework.data.util.TypeInformation, org.springframework.data.cassandra.core.convert.FrozenInfo)
*/
@Override
public CassandraColumnType resolve(TypeInformation<?> typeInformation, FrozenInfo frozen) {
private CassandraColumnType resolve(TypeInformation<?> typeInformation, FrozenIndicator frozen) {
return getCustomWriteTarget(typeInformation).map(it -> {
return createCassandraTypeDescriptor(tryResolve(it), ClassTypeInformation.from(it));
@@ -389,24 +391,23 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
return new DefaultCassandraColumnType(typeInformation, dataType);
}
private CassandraColumnType createCassandraTypeDescriptor(TypeInformation<?> typeInformation, FrozenInfo frozen) {
private CassandraColumnType createCassandraTypeDescriptor(TypeInformation<?> typeInformation,
FrozenIndicator frozen) {
if (List.class.isAssignableFrom(typeInformation.getType())) {
FrozenInfo elementFrozen = frozen.forFirstTypeParameter();
return ColumnType.listOf(resolve(typeInformation.getRequiredComponentType(), elementFrozen), frozen);
return ColumnType.listOf(resolve(typeInformation.getRequiredComponentType(), frozen.getFrozen(0)),
frozen.isFrozen());
}
if (Set.class.isAssignableFrom(typeInformation.getType())) {
FrozenInfo elementFrozen = frozen.forFirstTypeParameter();
return ColumnType.setOf(resolve(typeInformation.getRequiredComponentType(), elementFrozen), frozen);
return ColumnType.setOf(resolve(typeInformation.getRequiredComponentType(), frozen.getFrozen(0)),
frozen.isFrozen());
}
if (typeInformation.isMap()) {
FrozenInfo frozenKey = frozen.forFirstTypeParameter();
FrozenInfo frozenValue = frozen.forSecondTypeParameter();
FrozenIndicator frozenKey = frozen.getFrozen(0);
FrozenIndicator frozenValue = frozen.getFrozen(1);
return ColumnType.mapOf(resolve(typeInformation.getRequiredComponentType(), frozenKey),
resolve(typeInformation.getRequiredMapValueType(), frozenValue));
@@ -417,7 +418,7 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
if (persistentEntity != null) {
if (persistentEntity.isUserDefinedType()) {
return new DefaultCassandraColumnType(typeInformation, getUserType(persistentEntity, frozen.self));
return new DefaultCassandraColumnType(typeInformation, getUserType(persistentEntity, frozen.isFrozen()));
}
if (persistentEntity.isTupleType()) {

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2020 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.convert;
import java.util.ArrayList;
import java.util.List;
/**
* Information about if a type or its elements should be frozen or not.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 3.0
*/
class FrozenIndicator {
public static final FrozenIndicator NOT_FROZEN = new FrozenIndicator();
private final boolean frozen;
private final List<FrozenIndicator> nested = new ArrayList<>();
public static FrozenIndicator frozen(boolean frozen) {
return new FrozenIndicator(frozen);
}
private FrozenIndicator() {
this(false);
}
private FrozenIndicator(boolean frozen) {
this.frozen = frozen;
}
public void addNested(FrozenIndicator frozenIndicator) {
this.nested.add(frozenIndicator);
}
boolean isFrozen() {
return frozen;
}
FrozenIndicator getFrozen(int parameterIndex) {
return parameterIndex < this.nested.size() ? this.nested.get(parameterIndex) : NOT_FROZEN;
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2020 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.convert;
/**
* Information about if a type or its elements should be frozen or not.
*/
public class FrozenInfo {
public static final FrozenInfo NOT_FROZEN = new FrozenInfo();
final boolean self;
final boolean firstTypeParameter;
final boolean secondTypeParameter;
public static FrozenInfo frozen(boolean frozen) {
return new FrozenInfo(frozen);
}
private FrozenInfo() {
this(false, false, false);
}
private FrozenInfo(boolean selfFrozen) {
this(selfFrozen, false, false);
}
private FrozenInfo(boolean self, boolean firstTypeParameter, boolean secondTypeParameter) {
this.self = self;
this.firstTypeParameter = firstTypeParameter;
this.secondTypeParameter = secondTypeParameter;
}
FrozenInfo withFirstTypeParameter(boolean firstTypeParameter) {
return new FrozenInfo(self, firstTypeParameter, secondTypeParameter);
}
FrozenInfo withSecondTypeParameter(boolean secondTypeParameter) {
return new FrozenInfo(self, firstTypeParameter, secondTypeParameter);
}
FrozenInfo forFirstTypeParameter() {
return new FrozenInfo(firstTypeParameter);
}
FrozenInfo forSecondTypeParameter() {
return new FrozenInfo(secondTypeParameter);
}
}

View File

@@ -22,15 +22,16 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies a type as "frozen".
* Indicates that a persistent property should use a {@code frozen} column type.
*
* @author Jens Schauder
* @see <a href="https://express-cassandra.readthedocs.io/en/stable/advanced/#frozen-collections">Documentation about frozen collections</a>
* @see <a href="https://express-cassandra.readthedocs.io/en/stable/advanced/#frozen-collections">Documentation about
* frozen collections</a>
* @since 3.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE_USE, ElementType.FIELD, ElementType.METHOD})
@Target(value = { ElementType.TYPE_USE, ElementType.FIELD, ElementType.METHOD })
public @interface Frozen {
}

View File

@@ -46,6 +46,7 @@ import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
* Unit tests for {@link DefaultColumnTypeResolver}.
*
* @author Mark Paluch
* @author Jens Schauder
*/
public class ColumnTypeResolverUnitTests {

View File

@@ -8,6 +8,7 @@ This chapter summarizes changes and new features for each release.
* Upgrade to Cassandra Driver version 4. See the <<cassandra.migration.2.x-to-3.x,2.x to 3.x migration guide for details>>.
* Support for `NamingStrategy`.
* Support for frozen collections and UDT columns in schema creation.
[[new-features.2-2-0]]
== What's new in Spring Data for Apache Cassandra 2.2

View File

@@ -377,6 +377,8 @@ Describes the index to be created at session initialization.
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.
* `@Frozen`: Applied at the field level to class-types and parametrized types.
Declares a frozen UDT column or frozen collection like `List<@Frozen UserDefinedPersonType>`.
* `@UserDefinedType`: Applied at the type level to specify a Cassandra User-defined Data Type (UDT).
Types are derived from the declaration by default.
* `@Tuple`: Applied at the type level to use a type as a mapped tuple.