diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java index a6dde5c2e..78b3383b9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java @@ -63,8 +63,12 @@ public class BasicCassandraMappingContext implements CassandraMappingContext, ApplicationContextAware { protected ApplicationContext context; + + protected CassandraPersistentEntityMetadataVerifier verifier = + new CompositeCassandraPersistentEntityMetadataVerifier(); + protected ClassLoader beanClassLoader; - protected CassandraPersistentEntityMetadataVerifier verifier = new CompositeCassandraPersistentEntityMetadataVerifier(); + protected Mapping mapping = new Mapping(); // useful caches @@ -194,17 +198,20 @@ public class BasicCassandraMappingContext if (property.isCompositePrimaryKey()) { - CassandraPersistentEntity pkEntity = getPersistentEntity(property.getRawType()); + CassandraPersistentEntity primaryKeyEntity = getPersistentEntity(property.getRawType()); - pkEntity.doWithProperties(new PropertyHandler() { + primaryKeyEntity.doWithProperties(new PropertyHandler() { @Override - public void doWithPersistentProperty(CassandraPersistentProperty pkProp) { + public void doWithPersistentProperty(CassandraPersistentProperty primaryKeyProperty) { - if (pkProp.isPartitionKeyColumn()) { - spec.partitionKeyColumn(pkProp.getColumnName(), getDataType(pkProp)); - } else { // it's a cluster column - spec.clusteredKeyColumn(pkProp.getColumnName(), getDataType(pkProp), pkProp.getPrimaryKeyOrdering()); + if (primaryKeyProperty.isPartitionKeyColumn()) { + spec.partitionKeyColumn(primaryKeyProperty.getColumnName(), getDataType(primaryKeyProperty)); + } + else { // it's a cluster column + spec.clusteredKeyColumn(primaryKeyProperty.getColumnName(), + getDataType(primaryKeyProperty), + primaryKeyProperty.getPrimaryKeyOrdering()); } } }); @@ -212,9 +219,11 @@ public class BasicCassandraMappingContext } else { if (property.isIdProperty() || property.isPartitionKeyColumn()) { spec.partitionKeyColumn(property.getColumnName(), getDataType(property)); - } else if (property.isClusterKeyColumn()) { + } + else if (property.isClusterKeyColumn()) { spec.clusteredKeyColumn(property.getColumnName(), getDataType(property), property.getPrimaryKeyOrdering()); - } else { + } + else { spec.column(property.getColumnName(), getDataType(property)); } } @@ -233,7 +242,6 @@ public class BasicCassandraMappingContext */ @Override protected boolean shouldCreatePersistentEntityFor(TypeInformation typeInfo) { - return (!customConversions.hasCustomWriteTarget(typeInfo.getType()) && super.shouldCreatePersistentEntityFor(typeInfo)); } @@ -243,7 +251,6 @@ public class BasicCassandraMappingContext */ @Override protected CassandraPersistentEntity addPersistentEntity(TypeInformation typeInformation) { - // Prevent conversion types created as CassandraPersistentEntity return (shouldCreatePersistentEntityFor(typeInformation) ? super.addPersistentEntity(typeInformation) : null); } @@ -289,7 +296,6 @@ public class BasicCassandraMappingContext */ @Override public DataType getDataType(Class type) { - return (customConversions.hasCustomWriteTarget(type) ? getDataTypeFor(customConversions.getCustomWriteTarget(type)) : getDataTypeFor(type)); } @@ -300,10 +306,10 @@ public class BasicCassandraMappingContext this.mapping = mapping; } + @SuppressWarnings("all") protected void processMappingOverrides() { if (mapping != null) { for (EntityMapping entityMapping : mapping.getEntityMappings()) { - if (entityMapping != null) { String entityClassName = entityMapping.getEntityClassName(); @@ -312,10 +318,8 @@ public class BasicCassandraMappingContext CassandraPersistentEntity entity = getPersistentEntity(entityClass); - if (entity == null) { - throw new IllegalStateException(String.format( - "Unknown persistent entity class name [%s]", entityClassName)); - } + Assert.state(entity != null, String.format("Unknown persistent entity class name [%s]", + entityClassName)); String tableName = entityMapping.getTableName(); @@ -377,7 +381,7 @@ public class BasicCassandraMappingContext return entity; } - throw new IllegalArgumentException(String.format("unknown persistent type [%s]", type.getName())); + throw new IllegalArgumentException(String.format("Unknown persistent type [%s]", type.getName())); } @Override diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java index a12b39eb2..f54a0f161 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java @@ -48,14 +48,20 @@ import org.springframework.util.StringUtils; public class BasicCassandraPersistentEntity extends BasicPersistentEntity implements CassandraPersistentEntity, ApplicationContextAware { - protected static final CassandraPersistentEntityMetadataVerifier DEFAULT_VERIFIER = new CompositeCassandraPersistentEntityMetadataVerifier(); + protected static final CassandraPersistentEntityMetadataVerifier DEFAULT_VERIFIER = + new CompositeCassandraPersistentEntityMetadataVerifier(); + + protected ApplicationContext context; + + protected Boolean forceQuote; + + protected CassandraMappingContext mappingContext; + + protected CassandraPersistentEntityMetadataVerifier verifier = DEFAULT_VERIFIER; protected CqlIdentifier tableName; - protected CassandraMappingContext mappingContext; + protected StandardEvaluationContext spelContext; - protected CassandraPersistentEntityMetadataVerifier verifier = DEFAULT_VERIFIER; - protected ApplicationContext context; - protected Boolean forceQuote; public BasicCassandraPersistentEntity(TypeInformation typeInformation) { this(typeInformation, null); @@ -110,50 +116,6 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity properties = new ArrayList(); - if (!isCompositePrimaryKey()) { - throw new IllegalStateException(String.format("[%s] does not represent a composite primary key class", - this.getType().getName())); - } + Assert.state(isCompositePrimaryKey(), String.format("[%s] does not represent a composite primary key class", + this.getType().getName())); addCompositePrimaryKeyProperties(this, properties); @@ -180,12 +140,12 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity() { @Override - public void doWithPersistentProperty(CassandraPersistentProperty p) { + public void doWithPersistentProperty(CassandraPersistentProperty property) { - if (p.isCompositePrimaryKey()) { - addCompositePrimaryKeyProperties(p.getCompositePrimaryKeyEntity(), properties); + if (property.isCompositePrimaryKey()) { + addCompositePrimaryKeyProperties(property.getCompositePrimaryKeyEntity(), properties); } else { - properties.add(p); + properties.add(property); } } }); @@ -194,16 +154,52 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity entity) throws MappingException { - for (CassandraPersistentEntityMetadataVerifier verifier : verifiers) { verifier.verify(entity); } @@ -75,26 +74,22 @@ public class CompositeCassandraPersistentEntityMetadataVerifier implements Cassa /** * {@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; + throw new VerifierMappingExceptions(entity, Collections.singletonList(new MappingException( + String.format("Cassandra entities must be annotated with either @%s, @%s, or @%s", + Persistent.class.getSimpleName(), Table.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName())))); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifier.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifier.java index 3d51b9b40..67e2437ba 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifier.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifier.java @@ -32,7 +32,7 @@ import org.springframework.data.mapping.model.MappingException; */ public class PrimaryKeyClassEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier { - /* + /* * (non-Javadoc) * @see org.springframework.data.cassandra.mapping.CassandraPersistentEntityMetadataVerifier#verify(org.springframework.data.cassandra.mapping.CassandraPersistentEntity) */ @@ -55,16 +55,15 @@ public class PrimaryKeyClassEntityMetadataVerifier implements CassandraPersisten // 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()))); + 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()))); + exceptions.add(new MappingException(String.format("@%s must only extend Object", + PrimaryKeyClass.class.getSimpleName()))); } - // Parse entity properties entity.doWithProperties(new PropertyHandler() { @Override @@ -83,37 +82,37 @@ public class PrimaryKeyClassEntityMetadataVerifier implements CassandraPersisten }); if (!compositePrimaryKeys.isEmpty()) { - exceptions - .add(new MappingException("Composite primary keys are not allowed inside of composite primary key classes")); + 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()))); + exceptions.add(new MappingException(String.format( + "Composite primary key type [%1$s] has no fields annotated with @%2$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()))); + 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()))); + exceptions.add(new MappingException(String.format( + "Annotations @%1$s and @%2$s are invalid for type annotated with @%3$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()))); + exceptions.add(new MappingException(String.format( + "Property [%1$s] annotated with @%2$s must be a simple CassandraType", property.getName(), + PrimaryKeyColumn.class.getSimpleName()))); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java index d98d27f2f..f06c78118 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java @@ -1,12 +1,12 @@ /* * 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. * 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. @@ -25,7 +25,7 @@ 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 */ @@ -33,11 +33,12 @@ import org.springframework.util.Assert; public class VerifierMappingExceptions extends MappingException { final Collection exceptions; + private final String className; /** * 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 @@ -46,7 +47,6 @@ public class VerifierMappingExceptions extends MappingException { 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(exceptions)); @@ -57,10 +57,11 @@ public class VerifierMappingExceptions extends MappingException { * Creates a new {@link VerifierMappingExceptions} for the given {@code entity} and message. * * @param entity must not be {@literal null}. - * @param s + * @param message */ - public VerifierMappingExceptions(CassandraPersistentEntity entity, String s) { - super(s); + public VerifierMappingExceptions(CassandraPersistentEntity entity, String message) { + + super(message); Assert.notNull(entity, "CassandraPersistentEntity must not be null"); @@ -82,7 +83,7 @@ public class VerifierMappingExceptions extends MappingException { /** * Returns a list of the MappingExceptions aggregated within. - * + * * @return The Collection of MappingException */ public Collection getMappingExceptions() { @@ -91,20 +92,22 @@ public class VerifierMappingExceptions extends MappingException { /** * Returns a list of the MappingException messages aggregated within. - * + * * @return The Collection of Messages */ public Collection getMessages() { Collection messages = new LinkedList(); + for (MappingException e : exceptions) { messages.add(e.getMessage()); } + return messages; } /** * Returns the number of errors that have been added to this Exception Class. - * + * * @return Number of Errors present */ public int getCount() { @@ -114,10 +117,11 @@ public class VerifierMappingExceptions extends MappingException { @Override public String getMessage() { StringBuilder builder = new StringBuilder(className).append(":\n"); + for (MappingException e : exceptions) { builder.append(" - ").append(e.getMessage()).append("\n"); } + return builder.toString(); } - } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityUnitTests.java index 83306656b..8c0273c0d 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityUnitTests.java @@ -15,14 +15,22 @@ */ package org.springframework.data.cassandra.mapping; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.isA; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.cassandra.core.cql.CqlIdentifier; import org.springframework.context.ApplicationContext; import org.springframework.data.util.ClassTypeInformation; @@ -31,6 +39,7 @@ import org.springframework.data.util.ClassTypeInformation; * * @author Alex Shvid * @author Matthew T. Adams + * @author John Blum */ @RunWith(MockitoJUnitRunner.class) public class BasicCassandraPersistentEntityUnitTests { @@ -70,6 +79,35 @@ public class BasicCassandraPersistentEntityUnitTests { assertThat(entity.getTableName().toCql(), is(bean.tableName)); } + @Test + public void setForceQuoteCallsSetTableName() { + BasicCassandraPersistentEntity entitySpy = + spy(new BasicCassandraPersistentEntity(ClassTypeInformation.from(Message.class))); + + entitySpy.tableName = CqlIdentifier.cqlId("Messages", false); + + assertThat(entitySpy.forceQuote, is(nullValue(Boolean.class))); + + entitySpy.setForceQuote(true); + + assertThat(entitySpy.forceQuote, is(true)); + + verify(entitySpy, times(1)).setTableName(isA(CqlIdentifier.class)); + } + + @Test + public void setForceQuoteDoesNothing() { + BasicCassandraPersistentEntity entitySpy = + spy(new BasicCassandraPersistentEntity(ClassTypeInformation.from(Message.class))); + + entitySpy.forceQuote = true; + entitySpy.setForceQuote(true); + + assertThat(entitySpy.forceQuote, is(true)); + + verify(entitySpy, never()).setTableName(isA(CqlIdentifier.class)); + } + @Table("messages") static class Message {}