DATACASS-258 - Split metadata verifier for Entities and PrimaryKey types.
We provide now two separate verifiers for Table types and PrimaryKey types. Exception messages are aligned between both verifiers and the checks use simplified code instead to perform checks. VerifierMappingExceptions introduces now immutability and methods introducing mutability are deprecated.
This commit is contained in:
@@ -63,11 +63,8 @@ public class BasicCassandraMappingContext
|
||||
implements CassandraMappingContext, ApplicationContextAware {
|
||||
|
||||
protected ApplicationContext context;
|
||||
|
||||
protected ClassLoader beanClassLoader;
|
||||
|
||||
protected CassandraPersistentEntityMetadataVerifier verifier = new BasicCassandraPersistentEntityMetadataVerifier();
|
||||
|
||||
protected CassandraPersistentEntityMetadataVerifier verifier = new CompositeCassandraPersistentEntityMetadataVerifier();
|
||||
protected Mapping mapping = new Mapping();
|
||||
|
||||
// useful caches
|
||||
|
||||
@@ -48,7 +48,7 @@ import org.springframework.util.StringUtils;
|
||||
public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T, CassandraPersistentProperty>
|
||||
implements CassandraPersistentEntity<T>, ApplicationContextAware {
|
||||
|
||||
protected static final CassandraPersistentEntityMetadataVerifier DEFAULT_VERIFIER = new BasicCassandraPersistentEntityMetadataVerifier();
|
||||
protected static final CassandraPersistentEntityMetadataVerifier DEFAULT_VERIFIER = new CompositeCassandraPersistentEntityMetadataVerifier();
|
||||
|
||||
protected CqlIdentifier tableName;
|
||||
protected CassandraMappingContext mappingContext;
|
||||
|
||||
@@ -15,66 +15,50 @@
|
||||
*/
|
||||
package org.springframework.data.cassandra.mapping;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
|
||||
/**
|
||||
* Default implementation for Cassandra Persistent Entity Verification. Ensures that annotated Persistent Entities will
|
||||
* map properly to a Cassandra Table.
|
||||
* Default implementation for Cassandra Persistent Entity Verification. Ensures that annotated
|
||||
* {@link CassandraPersistentEntity entities} will map properly to a Cassandra Table.
|
||||
*
|
||||
* @author Matthew T Adams
|
||||
* @author David Webb
|
||||
* @author John Blum
|
||||
* @author Mark Paluch
|
||||
* @see Table
|
||||
* @see PrimaryKey
|
||||
* @see Id
|
||||
*/
|
||||
public class BasicCassandraPersistentEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BasicCassandraPersistentEntityMetadataVerifier.class);
|
||||
|
||||
protected boolean strict = false;
|
||||
@Deprecated protected boolean strict = false;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.mapping.CassandraPersistentEntityMetadataVerifier#verify(org.springframework.data.cassandra.mapping.CassandraPersistentEntity)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {
|
||||
|
||||
if (entity.getType().isInterface()){
|
||||
if (entity.getType().isInterface() || entity.findAnnotation(Table.class) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entity, String.format(
|
||||
"Mapping Exceptions from BasicCassandraPersistentEntityMetadataVerifier for %s", entity.getName()));
|
||||
List<MappingException> exceptions = new ArrayList<MappingException>();
|
||||
|
||||
final List<CassandraPersistentProperty> idProperties = new ArrayList<CassandraPersistentProperty>();
|
||||
final List<CassandraPersistentProperty> compositePrimaryKeys = new ArrayList<CassandraPersistentProperty>();
|
||||
final List<CassandraPersistentProperty> partitionKeyColumns = new ArrayList<CassandraPersistentProperty>();
|
||||
final List<CassandraPersistentProperty> clusterKeyColumns = new ArrayList<CassandraPersistentProperty>();
|
||||
final List<CassandraPersistentProperty> primaryKeyColumns = new ArrayList<CassandraPersistentProperty>();
|
||||
|
||||
Class<?> entityType = entity.getType();
|
||||
|
||||
boolean isTable = (entityType.isAnnotationPresent(Table.class)
|
||||
|| entityType.isAnnotationPresent(Persistent.class));
|
||||
|
||||
boolean isPrimaryKeyClass = entityType.isAnnotationPresent(PrimaryKeyClass.class);
|
||||
|
||||
// Ensure entity is not both a @Table(@Persistent) and a @PrimaryKey
|
||||
if (isTable && isPrimaryKeyClass) {
|
||||
exceptions.add(new MappingException("Entity cannot be of type Table and PrimaryKey"));
|
||||
throw exceptions;
|
||||
}
|
||||
|
||||
// Ensure entity is either a @Table/@Persistent or a @PrimaryKey
|
||||
if (!isTable && !isPrimaryKeyClass) {
|
||||
exceptions.add(new MappingException(
|
||||
"Cassandra entities must have the @Table, @Persistent or @PrimaryKeyClass Annotation"));
|
||||
throw exceptions;
|
||||
// Ensure entity is not both a @Table(@Persistent) and a @PrimaryKeyClass
|
||||
if (entity.isCompositePrimaryKey()) {
|
||||
exceptions.add(new MappingException(String.format("Entity cannot be of type @%s and @%s",
|
||||
Table.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName())));
|
||||
}
|
||||
|
||||
// Parse entity properties
|
||||
@@ -85,10 +69,7 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra
|
||||
if (property.isIdProperty()) {
|
||||
idProperties.add(property);
|
||||
} else if (property.isClusterKeyColumn()) {
|
||||
clusterKeyColumns.add(property);
|
||||
primaryKeyColumns.add(property);
|
||||
} else if (property.isCompositePrimaryKey()) {
|
||||
compositePrimaryKeys.add(property);
|
||||
} else if (property.isPartitionKeyColumn()) {
|
||||
partitionKeyColumns.add(property);
|
||||
primaryKeyColumns.add(property);
|
||||
@@ -96,145 +77,87 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra
|
||||
}
|
||||
});
|
||||
|
||||
final int idPropertyCount = idProperties.size();
|
||||
final int partitionKeyColumnCount = partitionKeyColumns.size();
|
||||
final int primaryKeyColumnCount = primaryKeyColumns.size();
|
||||
|
||||
// Perform rules verification on PrimaryKeyClass
|
||||
if (isPrimaryKeyClass) {
|
||||
|
||||
// Must have at least 1 attribute annotated with @PrimaryKeyColumn
|
||||
if (primaryKeyColumnCount == 0) {
|
||||
exceptions.add(new MappingException(String.format(
|
||||
"Composite primary key type [%s] has no fields annotated with @%s", entity.getType().getName(),
|
||||
PrimaryKeyColumn.class.getSimpleName())));
|
||||
}
|
||||
|
||||
// At least one of the PrimaryKeyColumns must have a type PARTIONED
|
||||
if (partitionKeyColumnCount == 0) {
|
||||
exceptions.add(new MappingException(
|
||||
"At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED"));
|
||||
}
|
||||
|
||||
// Cannot have any Id or PrimaryKey Annotations
|
||||
if (idPropertyCount > 0) {
|
||||
exceptions.add(new MappingException(
|
||||
"Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass"));
|
||||
}
|
||||
|
||||
// Ensure that PrimaryKeyColumn is a supported Type.
|
||||
for (CassandraPersistentProperty property : primaryKeyColumns) {
|
||||
if (CassandraSimpleTypeHolder.getDataTypeFor(property.getType()) == null) {
|
||||
exceptions.add(new MappingException(
|
||||
"Fields annotated with @PrimaryKeyColumn must be simple CassandraTypes"));
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure PrimaryKeyClass is Serializable
|
||||
if (!Serializable.class.isAssignableFrom(entityType)) {
|
||||
exceptions.add(new MappingException("@PrimaryKeyClass must be Serializable"));
|
||||
}
|
||||
|
||||
// Ensure PrimaryKeyClass only extends Object
|
||||
if (!entityType.getSuperclass().equals(Object.class)) {
|
||||
exceptions.add(new MappingException("@PrimaryKeyClass must only extend Object"));
|
||||
}
|
||||
|
||||
// Check that PrimaryKeyClass overrides "boolean equals(Object)"
|
||||
verifyMethodPresent(entityType, "equals", "boolean equals(Object)", exceptions);
|
||||
|
||||
// Ensure PrimaryKeyClass overrides "int hashCode()"
|
||||
verifyMethodPresent(entityType, "hashCode", "int hashCode()", exceptions);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform rules verification on Table/Persistent
|
||||
*/
|
||||
if (isTable) {
|
||||
|
||||
// TODO Verify annotation values with CqlIndentifier
|
||||
// TODO Verify annotation values with CqlIndentifier
|
||||
|
||||
// Ensure only one PK or at least one partitioned PK Column and not both PK(s) & PK Column(s) exist
|
||||
if (primaryKeyColumnCount == 0) {
|
||||
// Can only have one PK
|
||||
if (idPropertyCount != 1) {
|
||||
exceptions.add(new MappingException(String.format(
|
||||
"@Table/@Persistent types must have only one @PrimaryKey attribute, if any; Found %s",
|
||||
idPropertyCount)));
|
||||
// Ensure only one PK or at least one partitioned PK Column and not both PK(s) & PK Column(s) exist
|
||||
if (primaryKeyColumns.isEmpty()) {
|
||||
|
||||
throw exceptions;
|
||||
}
|
||||
// Can only have one PK
|
||||
if (idProperties.size() != 1) {
|
||||
exceptions
|
||||
.add(new MappingException(String.format("@%s types must have only one primary attribute, if any; Found %s",
|
||||
Table.class.getSimpleName(), idProperties.size())));
|
||||
|
||||
// Ensure that Id is a supported Type. At this point there is only 1.
|
||||
Class<?> idType = idProperties.get(0).getType();
|
||||
fail(entity, exceptions);
|
||||
}
|
||||
|
||||
if (!idType.isAnnotationPresent(PrimaryKeyClass.class)
|
||||
&& CassandraSimpleTypeHolder.getDataTypeFor(idType) == null) {
|
||||
// Ensure that Id is a supported Type. At this point there is only 1.
|
||||
CassandraPersistentProperty idProperty = idProperties.get(0);
|
||||
Class<?> idType = idProperty.getType();
|
||||
|
||||
exceptions.add(new MappingException(
|
||||
"Fields annotated with @PrimaryKey must be simple CassandraTypes or @PrimaryKeyClass type"));
|
||||
}
|
||||
} else if (idPropertyCount > 0) {
|
||||
// Then we have both PK(s) & PK Column(s)
|
||||
if (!idType.isAnnotationPresent(PrimaryKeyClass.class)
|
||||
&& CassandraSimpleTypeHolder.getDataTypeFor(idType) == null) {
|
||||
|
||||
exceptions
|
||||
.add(new MappingException(String.format("Property [%s] annotated with @%s must be a simple CassandraType",
|
||||
idProperty.getName(), Id.class.getSimpleName())));
|
||||
}
|
||||
}
|
||||
|
||||
if (!idProperties.isEmpty() && !primaryKeyColumns.isEmpty()) {
|
||||
|
||||
// Then we have both PK(s) & PK Column(s)
|
||||
exceptions.add(new MappingException(String.format("@%s types must not define both @%s and @%s properties",
|
||||
Table.class.getSimpleName(), Id.class.getSimpleName(), PrimaryKeyColumn.class.getSimpleName())));
|
||||
|
||||
fail(entity, exceptions);
|
||||
}
|
||||
|
||||
// We have no PKs & only PK Column(s); ensure at least one is of type PARTITIONED
|
||||
if (!primaryKeyColumns.isEmpty() && partitionKeyColumns.isEmpty()) {
|
||||
exceptions
|
||||
.add(new MappingException(String.format("At least one of the @%s annotations must have a type of PARTITIONED",
|
||||
PrimaryKeyColumn.class.getSimpleName())));
|
||||
}
|
||||
|
||||
for (CassandraPersistentProperty property : primaryKeyColumns) {
|
||||
if (CassandraSimpleTypeHolder.getDataTypeFor(property.getType()) == null) {
|
||||
exceptions.add(new MappingException(String.format(
|
||||
"@Table/@Persistent types must not define both @PrimaryKeyColumn field(s) (found %s) and @PrimaryKey field(s) (found %s)",
|
||||
primaryKeyColumnCount, idPropertyCount)));
|
||||
|
||||
throw exceptions;
|
||||
} else {
|
||||
// We have no PKs & only PK Column(s); ensure at least one is of type PARTITIONED
|
||||
if (partitionKeyColumnCount == 0) {
|
||||
exceptions.add(new MappingException(String.format(
|
||||
"@Table/@Persistent types must define at least one @PrimaryKeyColumn of type PARTITIONED")));
|
||||
}
|
||||
"Property [%s] annotated with @PrimaryKeyColumn must be a simple CassandraType", property.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether or not to throw Exception based on errors found
|
||||
if (exceptions.getCount() > 0) {
|
||||
log.error("Exceptions while verifying PersistentEntity", exceptions);
|
||||
throw exceptions;
|
||||
}
|
||||
}
|
||||
|
||||
boolean verifyMethodPresent(Class<?> type, String methodName, String methodDescription,
|
||||
VerifierMappingExceptions exceptions) {
|
||||
try {
|
||||
Method method = type.getDeclaredMethod(methodName, Object.class);
|
||||
|
||||
if (method == null || !method.getDeclaringClass().equals(type)) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
String message = String.format(
|
||||
"@PrimaryKeyClass should override '%s' method and use all @PrimaryKeyColumn fields",
|
||||
methodDescription);
|
||||
|
||||
if (strict) {
|
||||
exceptions.add(new MappingException(message, e));
|
||||
} else {
|
||||
log.warn(message);
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!exceptions.isEmpty()) {
|
||||
fail(entity, exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the setting for strict.
|
||||
* @deprecated Will be removed in future versions.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public boolean isStrict() {
|
||||
return strict;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param strict boolean setting for strict.
|
||||
* @deprecated Will be removed in future versions.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public void setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
private static void fail(CassandraPersistentEntity<?> entity, List<MappingException> exceptions) {
|
||||
throw new VerifierMappingExceptions(entity, exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.mapping;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Composite {@link CassandraPersistentEntityMetadataVerifier} to verify persistent entities and primary key classes.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.5
|
||||
* @see BasicCassandraPersistentEntityMetadataVerifier
|
||||
* @see PrimaryKeyClassEntityMetadataVerifier
|
||||
*/
|
||||
public class CompositeCassandraPersistentEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier {
|
||||
|
||||
private Collection<CassandraPersistentEntityMetadataVerifier> verifiers;
|
||||
|
||||
/**
|
||||
* Creates a new {@link CompositeCassandraPersistentEntityMetadataVerifier} using default entity and primary key
|
||||
* verifiers.
|
||||
*
|
||||
* @see BasicCassandraPersistentEntityMetadataVerifier
|
||||
* @see PrimaryKeyClassEntityMetadataVerifier
|
||||
*/
|
||||
public CompositeCassandraPersistentEntityMetadataVerifier() {
|
||||
this(Arrays.asList(new PersistentAnnotationVerifier(), //
|
||||
new PrimaryKeyClassEntityMetadataVerifier(), //
|
||||
new BasicCassandraPersistentEntityMetadataVerifier()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link CompositeCassandraPersistentEntityMetadataVerifier} for the given {@code verifiers}
|
||||
*
|
||||
* @param verifiers must not be {@literal null}.
|
||||
*/
|
||||
private CompositeCassandraPersistentEntityMetadataVerifier(
|
||||
Collection<CassandraPersistentEntityMetadataVerifier> verifiers) {
|
||||
|
||||
Assert.notNull(verifiers, "Verifiers must not be null");
|
||||
|
||||
this.verifiers = verifiers;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.mapping.CassandraPersistentEntityMetadataVerifier#verify(org.springframework.data.cassandra.mapping.CassandraPersistentEntity)
|
||||
*/
|
||||
@Override
|
||||
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {
|
||||
|
||||
for (CassandraPersistentEntityMetadataVerifier verifier : verifiers) {
|
||||
verifier.verify(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CassandraPersistentEntityMetadataVerifier} implementation that requires classes to be annotated with
|
||||
* {@link Persistent}, {@link Table} or {@link PrimaryKeyClass}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class PersistentAnnotationVerifier implements CassandraPersistentEntityMetadataVerifier {
|
||||
|
||||
@Override
|
||||
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {
|
||||
|
||||
if (entity.getType().isInterface()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure entity is either a @Table/@Persistent or a @PrimaryKey
|
||||
if (entity.findAnnotation(Persistent.class) == null) {
|
||||
|
||||
VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entity,
|
||||
Arrays.asList(new MappingException(String.format(
|
||||
"Cassandra entities must be annotated with either @%s, @%s, or @%s", Persistent.class.getSimpleName(),
|
||||
Table.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName()))));
|
||||
throw exceptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
|
||||
/**
|
||||
* {@link CassandraPersistentEntityMetadataVerifier} for {@link PrimaryKeyClass} entities. Ensures a valid mapping for
|
||||
* composite primary keys.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.5
|
||||
* @see PrimaryKeyClass
|
||||
*/
|
||||
public class PrimaryKeyClassEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.mapping.CassandraPersistentEntityMetadataVerifier#verify(org.springframework.data.cassandra.mapping.CassandraPersistentEntity)
|
||||
*/
|
||||
@Override
|
||||
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {
|
||||
|
||||
if (entity.getType().isInterface() || !entity.isCompositePrimaryKey()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<MappingException> exceptions = new ArrayList<MappingException>();
|
||||
|
||||
final List<CassandraPersistentProperty> idProperties = new ArrayList<CassandraPersistentProperty>();
|
||||
final List<CassandraPersistentProperty> compositePrimaryKeys = new ArrayList<CassandraPersistentProperty>();
|
||||
final List<CassandraPersistentProperty> partitionKeyColumns = new ArrayList<CassandraPersistentProperty>();
|
||||
final List<CassandraPersistentProperty> primaryKeyColumns = new ArrayList<CassandraPersistentProperty>();
|
||||
|
||||
Class<?> entityType = entity.getType();
|
||||
|
||||
// Ensure entity is not both a @Table(@Persistent) and a @PrimaryKey
|
||||
if (entity.findAnnotation(Table.class) != null) {
|
||||
exceptions.add(new MappingException(String.format("Entity cannot be of type @%s and @%s",
|
||||
Table.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName())));
|
||||
}
|
||||
|
||||
// Ensure PrimaryKeyClass only extends Object
|
||||
if (!entityType.getSuperclass().equals(Object.class)) {
|
||||
exceptions.add(
|
||||
new MappingException(String.format("@%s must only extend Object", PrimaryKeyClass.class.getSimpleName())));
|
||||
}
|
||||
|
||||
// Parse entity properties
|
||||
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
|
||||
|
||||
@Override
|
||||
public void doWithPersistentProperty(CassandraPersistentProperty property) {
|
||||
if (property.isCompositePrimaryKey()) {
|
||||
compositePrimaryKeys.add(property);
|
||||
} else if (property.isIdProperty()) {
|
||||
idProperties.add(property);
|
||||
} else if (property.isClusterKeyColumn()) {
|
||||
primaryKeyColumns.add(property);
|
||||
} else if (property.isPartitionKeyColumn()) {
|
||||
partitionKeyColumns.add(property);
|
||||
primaryKeyColumns.add(property);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!compositePrimaryKeys.isEmpty()) {
|
||||
exceptions
|
||||
.add(new MappingException("Composite primary keys are not allowed inside of composite primary key classes"));
|
||||
}
|
||||
|
||||
// Must have at least 1 attribute annotated with @PrimaryKeyColumn
|
||||
if (primaryKeyColumns.isEmpty()) {
|
||||
exceptions
|
||||
.add(new MappingException(String.format("Composite primary key type [%s] has no fields annotated with @%s",
|
||||
entity.getType().getName(), PrimaryKeyColumn.class.getSimpleName())));
|
||||
}
|
||||
|
||||
// At least one of the PrimaryKeyColumns must have a type PARTIONED
|
||||
if (partitionKeyColumns.isEmpty()) {
|
||||
exceptions
|
||||
.add(new MappingException(String.format("At least one of the @%s annotations must have a type of PARTITIONED",
|
||||
PrimaryKeyColumn.class.getSimpleName())));
|
||||
}
|
||||
|
||||
// Cannot have any Id or PrimaryKey Annotations
|
||||
if (!idProperties.isEmpty()) {
|
||||
exceptions
|
||||
.add(new MappingException(String.format("Annotations @%s and @%s are invalid for type annotated with @%s",
|
||||
Id.class.getSimpleName(), PrimaryKey.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName())));
|
||||
}
|
||||
|
||||
// Ensure that PrimaryKeyColumn is a supported Type.
|
||||
for (CassandraPersistentProperty property : primaryKeyColumns) {
|
||||
if (CassandraSimpleTypeHolder.getDataTypeFor(property.getType()) == null) {
|
||||
exceptions
|
||||
.add(new MappingException(String.format("Property [%s] annotated with @%s must be a simple CassandraType",
|
||||
property.getName(), PrimaryKeyColumn.class.getSimpleName())));
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether or not to throw Exception based on errors found
|
||||
if (!exceptions.isEmpty()) {
|
||||
fail(entity, exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fail(CassandraPersistentEntity<?> entity, List<MappingException> exceptions) {
|
||||
throw new VerifierMappingExceptions(entity, exceptions);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors
|
||||
* Copyright 2013-2016 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.
|
||||
@@ -16,35 +16,68 @@
|
||||
package org.springframework.data.cassandra.mapping;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Aggregator of multiple {@link MappingException} for convenience when verifying persistent entities. This allows the
|
||||
* framework to communicate all verification errors to the user of the framework, rather than one at a time.
|
||||
*
|
||||
* @author David Webb
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class VerifierMappingExceptions extends MappingException {
|
||||
|
||||
Collection<MappingException> exceptions = new LinkedList<MappingException>();
|
||||
private String className;
|
||||
final Collection<MappingException> exceptions;
|
||||
private final String className;
|
||||
|
||||
/**
|
||||
* @param s
|
||||
* Creates a new {@link VerifierMappingExceptions} for the given {@code entity} and message.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param exceptions must not be {@literal null}.
|
||||
* @since 1.5
|
||||
*/
|
||||
public VerifierMappingExceptions(CassandraPersistentEntity<?> entity, String s) {
|
||||
super(s);
|
||||
public VerifierMappingExceptions(CassandraPersistentEntity<?> entity, Collection<MappingException> exceptions) {
|
||||
|
||||
super(String.format("Mapping Exceptions for %s", entity.getName()));
|
||||
|
||||
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
|
||||
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
|
||||
|
||||
this.exceptions = Collections.unmodifiableCollection(new LinkedList<MappingException>(exceptions));
|
||||
this.className = entity.getType().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link VerifierMappingExceptions} for the given {@code entity} and message.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param s
|
||||
*/
|
||||
public void add(MappingException e) {
|
||||
exceptions.add(e);
|
||||
public VerifierMappingExceptions(CassandraPersistentEntity<?> entity, String s) {
|
||||
super(s);
|
||||
|
||||
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
|
||||
|
||||
this.exceptions = new LinkedList<MappingException>();
|
||||
this.className = entity.getType().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mappingException must not be {@literal null}.
|
||||
* @deprecated Exceptions should be immutable so this method is subject to be removed in future versions
|
||||
*/
|
||||
@Deprecated
|
||||
public void add(MappingException mappingException) {
|
||||
|
||||
Assert.notNull(mappingException, "MappingException must not be null");
|
||||
|
||||
exceptions.add(mappingException);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,7 +86,7 @@ public class VerifierMappingExceptions extends MappingException {
|
||||
* @return The Collection of MappingException
|
||||
*/
|
||||
public Collection<MappingException> getMappingExceptions() {
|
||||
return exceptions;
|
||||
return Collections.unmodifiableCollection(exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +115,7 @@ public class VerifierMappingExceptions extends MappingException {
|
||||
public String getMessage() {
|
||||
StringBuilder builder = new StringBuilder(className).append(":\n");
|
||||
for (MappingException e : exceptions) {
|
||||
builder.append(e.getMessage()).append("\n");
|
||||
builder.append(" - ").append(e.getMessage()).append("\n");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@@ -15,21 +15,16 @@
|
||||
*/
|
||||
package org.springframework.data.cassandra.mapping;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cassandra.core.Ordering;
|
||||
import org.springframework.cassandra.core.PrimaryKeyType;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntityMetadataVerifier}
|
||||
* through {@link CassandraMappingContext}
|
||||
@@ -39,120 +34,181 @@ import ch.qos.logback.classic.LoggerContext;
|
||||
*/
|
||||
public class BasicCassandraPersistentEntityMetadataVerifierUnitTests {
|
||||
|
||||
private static LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
private Logger logger = loggerContext.getLogger(BasicCassandraPersistentEntityMetadataVerifier.class);
|
||||
private CassandraMappingContext mappingContext;
|
||||
private BasicCassandraPersistentEntityMetadataVerifier verifier = new BasicCassandraPersistentEntityMetadataVerifier();
|
||||
private BasicCassandraMappingContext context = new BasicCassandraMappingContext();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mappingContext = new BasicCassandraMappingContext();
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class)
|
||||
public void testNonPersistentType() {
|
||||
mappingContext.getPersistentEntity(NonPersistentClass.class);
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class)
|
||||
public void testTooManyAnnotations() {
|
||||
mappingContext.getPersistentEntity(TooManyAnnotations.class);
|
||||
public void setUp() throws Exception {
|
||||
context.setVerifier(new NoOpVerifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testNonPrimaryKeyClass() {
|
||||
mappingContext.getPersistentEntity(Person.class);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class)
|
||||
public void testPrimaryKeyClassNotFullyImplemented() {
|
||||
mappingContext.getPersistentEntity(AnimalPkNoOverrides.class);
|
||||
public void shouldAllowInterfaceTypes() {
|
||||
verifier.verify(getEntity(MyInterface.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testPrimaryKeyClass() {
|
||||
|
||||
mappingContext.getPersistentEntity(AnimalPK.class);
|
||||
mappingContext.getPersistentEntity(Animal.class);
|
||||
verifier.verify(getEntity(Animal.class));
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class)
|
||||
public void testNoPartitionKey() {
|
||||
mappingContext.getPersistentEntity(NoPartitionKey.class);
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testNonPrimaryKeyClass() {
|
||||
verifier.verify(getEntity(Person.class));
|
||||
}
|
||||
|
||||
@Test(expected = MappingException.class)
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testNonPersistentType() {
|
||||
verifier.verify(getEntity(NonPersistentClass.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithPersistentAndPrimaryKeyClassAnnotations() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(TooManyAnnotations.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(), containsString("Entity cannot be of type @Table and @PrimaryKeyClass"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithComplexTypePrimaryKey() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(EntityWithComplexTypePrimaryKey.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("Property [species] annotated with @PrimaryKeyColumn must be a simple CassandraType"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithComplexTypeId() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(EntityWithComplexTypeId.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(), containsString("Property [species] annotated with @Id must be a simple CassandraType"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithoutPartitionKey() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(NoPartitionKey.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithoutPrimaryKey() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(NoPrimaryKey.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(), containsString("@Table types must have only one primary attribute, if any; Found 0"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testPkAndPkc() {
|
||||
mappingContext.getPersistentEntity(PkAndPkc.class);
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(PrimaryKeyAndPrimaryKeyColumn.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("@Table types must not define both @Id and @PrimaryKeyColumn properties"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnePkc() {
|
||||
|
||||
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(OnePkc.class);
|
||||
|
||||
assertNull(entity.getIdProperty());
|
||||
private CassandraPersistentEntity<?> getEntity(Class<?> entityClass) {
|
||||
return context.getPersistentEntity(entityClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPkc() {
|
||||
|
||||
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(MultiPkc.class);
|
||||
|
||||
assertNull(entity.getIdProperty());
|
||||
}
|
||||
interface MyInterface {}
|
||||
|
||||
static class NonPersistentClass {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
private String foo;
|
||||
private String bar;
|
||||
@Id String id;
|
||||
|
||||
String foo;
|
||||
String bar;
|
||||
}
|
||||
|
||||
@Table
|
||||
static class Person {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
@Id String id;
|
||||
|
||||
String firstName;
|
||||
String lastName;
|
||||
}
|
||||
|
||||
@Table
|
||||
static class Animal {
|
||||
|
||||
@PrimaryKey AnimalPK key;
|
||||
private String name;
|
||||
String name;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class AnimalPK implements Serializable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
static class AnimalPK {
|
||||
|
||||
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species;
|
||||
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed;
|
||||
@PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color;
|
||||
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class AnimalPkNoOverrides {
|
||||
@Table
|
||||
static class EntityWithComplexTypePrimaryKey {
|
||||
|
||||
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species;
|
||||
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed;
|
||||
@PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color;
|
||||
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object species;
|
||||
}
|
||||
|
||||
@Table
|
||||
static class EntityWithComplexTypeId {
|
||||
|
||||
@Id Object species;
|
||||
}
|
||||
|
||||
@Table
|
||||
@@ -160,28 +216,37 @@ public class BasicCassandraPersistentEntityMetadataVerifierUnitTests {
|
||||
static class TooManyAnnotations {}
|
||||
|
||||
@Table
|
||||
public static class NoPartitionKey {
|
||||
static class NoPartitionKey {
|
||||
|
||||
@PrimaryKeyColumn(ordinal = 0) String key;
|
||||
}
|
||||
|
||||
@Table
|
||||
public static class PkAndPkc {
|
||||
static class NoPrimaryKey {}
|
||||
|
||||
@Table
|
||||
static class PrimaryKeyAndPrimaryKeyColumn {
|
||||
|
||||
@PrimaryKey String primaryKey;
|
||||
@PrimaryKeyColumn(ordinal = 0) String primaryKeyColumn;
|
||||
}
|
||||
|
||||
@Table
|
||||
public static class OnePkc {
|
||||
static class OnePrimaryKeyColumn {
|
||||
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk;
|
||||
}
|
||||
|
||||
@Table
|
||||
public static class MultiPkc {
|
||||
static class MultiplePrimaryKeyColumns {
|
||||
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk0;
|
||||
@PrimaryKeyColumn(ordinal = 1) String pk1;
|
||||
}
|
||||
|
||||
private static class NoOpVerifier implements CassandraPersistentEntityMetadataVerifier {
|
||||
|
||||
@Override
|
||||
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.mapping;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cassandra.core.Ordering;
|
||||
import org.springframework.cassandra.core.PrimaryKeyType;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CompositeCassandraPersistentEntityMetadataVerifier}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class CompositeCassandraPersistentEntityMetadataVerifierUnitTests {
|
||||
|
||||
private CompositeCassandraPersistentEntityMetadataVerifier verifier = new CompositeCassandraPersistentEntityMetadataVerifier();
|
||||
private BasicCassandraMappingContext context = new BasicCassandraMappingContext();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
context.setVerifier(verifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowInterfaceTypes() {
|
||||
verifier.verify(getEntity(MyInterface.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testPrimaryKeyClass() {
|
||||
verifier.verify(getEntity(Animal.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testNonPrimaryKeyClass() {
|
||||
verifier.verify(getEntity(Person.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithNonPersistentClasses() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(NonPersistentClass.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(), containsString("Cassandra entities must be annotated with either @Persistent, @Table, or @PrimaryKeyClass"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithPersistentAndPrimaryKeyClassAnnotations() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(TooManyAnnotations.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(), containsString("Entity cannot be of type @Table and @PrimaryKeyClass"));
|
||||
}
|
||||
}
|
||||
|
||||
private CassandraPersistentEntity<?> getEntity(Class<?> entityClass) {
|
||||
return context.getPersistentEntity(entityClass);
|
||||
}
|
||||
|
||||
interface MyInterface {}
|
||||
|
||||
static class NonPersistentClass {
|
||||
|
||||
@Id String id;
|
||||
|
||||
String foo;
|
||||
String bar;
|
||||
}
|
||||
|
||||
@Table
|
||||
@PrimaryKeyClass
|
||||
static class TooManyAnnotations {}
|
||||
|
||||
@Table
|
||||
static class Person {
|
||||
|
||||
@Id String id;
|
||||
|
||||
String firstName;
|
||||
String lastName;
|
||||
}
|
||||
|
||||
@Table
|
||||
static class Animal {
|
||||
|
||||
@PrimaryKey AnimalPK key;
|
||||
private String name;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class AnimalPK {
|
||||
|
||||
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species;
|
||||
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed;
|
||||
@PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.mapping;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cassandra.core.Ordering;
|
||||
import org.springframework.cassandra.core.PrimaryKeyType;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PrimaryKeyClassEntityMetadataVerifier}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PrimaryKeyClassEntityMetadataVerifierUnitTests {
|
||||
|
||||
private PrimaryKeyClassEntityMetadataVerifier verifier = new PrimaryKeyClassEntityMetadataVerifier();
|
||||
private BasicCassandraMappingContext context = new BasicCassandraMappingContext();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
context.setVerifier(new NoOpVerifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowNonPersistentClasses() {
|
||||
verifier.verify(getEntity(NonPersistentClass.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowInterfaceTypes() {
|
||||
verifier.verify(getEntity(MyInterface.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowTableClass() {
|
||||
verifier.verify(getEntity(Person.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldVerifyPrimaryKeyClass() {
|
||||
verifier.verify(getEntity(AnimalPK.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithPersistentAndPrimaryKeyClassAnnotations() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(TooManyAnnotations.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(), containsString("Entity cannot be of type @Table and @PrimaryKeyClass"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithoutPartitionKey() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(NoPartitionKey.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithoutPrimaryKey() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(NoPrimaryKey.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailOnPrimaryKeyCycles() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(TypeCycle.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("Composite primary keys are not allowed inside of composite primary key classes"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithNestedPrimaryKeyClassReference() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(PKClassWithNestedCompositeKey.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("Composite primary keys are not allowed inside of composite primary key classes"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithComplexType() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(PKWithComplexType.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("Property [species] annotated with @PrimaryKeyColumn must be a simple CassandraType"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailWithPrimaryKeyClassAndPrimaryKeyAnnotations() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(PrimaryKeyAndPrimaryKeyColumn.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(),
|
||||
containsString("Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailForPrimaryKeyDerivedFromOtherThanObject() {
|
||||
|
||||
try {
|
||||
verifier.verify(getEntity(SubclassPK.class));
|
||||
fail("Missing MappingException");
|
||||
} catch (MappingException e) {
|
||||
assertThat(e.toString(), containsString("@PrimaryKeyClass must only extend Object"));
|
||||
}
|
||||
}
|
||||
|
||||
private CassandraPersistentEntity<?> getEntity(Class<?> entityClass) {
|
||||
return context.getPersistentEntity(entityClass);
|
||||
}
|
||||
|
||||
interface MyInterface {}
|
||||
|
||||
static class NonPersistentClass {
|
||||
|
||||
@Id String id;
|
||||
|
||||
String foo;
|
||||
String bar;
|
||||
}
|
||||
|
||||
@Table
|
||||
static class Person {
|
||||
|
||||
@Id String id;
|
||||
|
||||
String firstName;
|
||||
String lastName;
|
||||
}
|
||||
|
||||
@Table
|
||||
static class Animal {
|
||||
|
||||
@PrimaryKey AnimalPK key;
|
||||
private String name;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class AnimalPK {
|
||||
|
||||
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species;
|
||||
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed;
|
||||
@PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class PKWithComplexType {
|
||||
|
||||
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object species;
|
||||
}
|
||||
|
||||
@Table
|
||||
@PrimaryKeyClass
|
||||
static class TooManyAnnotations {}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class NoPartitionKey {
|
||||
|
||||
@PrimaryKeyColumn(ordinal = 0) String key;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class NoPrimaryKey {
|
||||
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class TypeCycle {
|
||||
|
||||
@PrimaryKey TypeCycle typeCycle;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class PKClassWithNestedCompositeKey {
|
||||
|
||||
@PrimaryKey OnePrimaryKeyColumn pkc;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class PrimaryKeyAndPrimaryKeyColumn {
|
||||
|
||||
@PrimaryKey String primaryKey;
|
||||
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String primaryKeyColumn;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class OnePrimaryKeyColumn {
|
||||
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk;
|
||||
}
|
||||
|
||||
static class MultiPrimaryKeyColumns {
|
||||
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk0;
|
||||
@PrimaryKeyColumn(ordinal = 1) String pk1;
|
||||
}
|
||||
|
||||
@PrimaryKeyClass
|
||||
static class SubclassPK extends MultiPrimaryKeyColumns {
|
||||
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk;
|
||||
}
|
||||
|
||||
private static class NoOpVerifier implements CassandraPersistentEntityMetadataVerifier {
|
||||
|
||||
@Override
|
||||
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.mapping;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link VerifierMappingExceptions}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class VerifierMappingExceptionsUnitTests {
|
||||
|
||||
@Mock CassandraPersistentEntity<?> entityMock;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setUp() throws Exception {
|
||||
|
||||
when(entityMock.getType()).thenReturn((Class) VerifierMappingExceptionsUnitTests.class);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACASS-258
|
||||
*/
|
||||
@Test
|
||||
public void testDeprecatedMutability() {
|
||||
|
||||
VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entityMock, "err");
|
||||
exceptions.add(new MappingException("my error"));
|
||||
|
||||
assertThat(exceptions.toString(), containsString("my error"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user