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:
Mark Paluch
2016-08-29 15:22:47 +02:00
committed by John Blum
parent 8c39fa4787
commit c3700eede3
10 changed files with 978 additions and 241 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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 {}
}
}

View File

@@ -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;
}
}

View File

@@ -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 {}
}
}

View File

@@ -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"));
}
}