From 847db1a52b068617ab80f46d4f1b42c1c8181925 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 28 Nov 2016 15:02:36 +0100 Subject: [PATCH] DATACASS-259 - Allow usage of Spring 4.2 @AliasFor for Cassandra annotations. We now support @AliasFor to build composed annotations with @Table, @UserDefinedType, @PrimaryKey, @PrimaryKeyClass, @PrimaryKeyColumn, @Column, @Query, @CassandraType. Original pull request: #90. --- .../BasicCassandraPersistentEntity.java | 11 +- .../cassandra/mapping/PrimaryKeyColumn.java | 11 +- .../data/cassandra/repository/Query.java | 3 +- .../repository/query/CassandraParameters.java | 51 ++++++- ...sicCassandraPersistentEntityUnitTests.java | 51 +++++++ ...cCassandraPersistentPropertyUnitTests.java | 140 +++++++++++++++--- ...ndraUserTypePersistentEntityUnitTests.java | 29 ++++ .../query/CassandraParametersUnitTests.java | 21 +++ .../StringBasedCassandraQueryUnitTests.java | 25 ++++ 9 files changed, 307 insertions(+), 35 deletions(-) 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 1590a882a..cc26f77f0 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 @@ -98,13 +98,10 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity potentiallyUnwrapParameterType(MethodParameter parameter) { @@ -134,7 +139,6 @@ public class CassandraParameters extends Parameters T getAnnotation(Class annotationClass) { + return methodParameter.getParameterAnnotation(annotationClass); + } + + /* (non-Javadoc) + * @see java.lang.reflect.AnnotatedElement#getAnnotations() + */ + @Override + public Annotation[] getAnnotations() { + return methodParameter.getParameterAnnotations(); + } + + /* (non-Javadoc) + * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations() + */ + @Override + public Annotation[] getDeclaredAnnotations() { + return methodParameter.getParameterAnnotations(); + } + } } 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 a66f75e02..c7a9828c4 100755 --- 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 @@ -18,12 +18,18 @@ package org.springframework.data.cassandra.mapping; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + 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.core.annotation.AliasFor; import org.springframework.data.util.ClassTypeInformation; /** @@ -113,6 +119,30 @@ public class BasicCassandraPersistentEntityUnitTests { assertThat(entity.isUserDefinedType()).isFalse(); } + /** + * @see DATACASS-259 + */ + @Test + public void shouldConsiderComposedTableAnnotation() { + + BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity( + ClassTypeInformation.from(TableWithComposedAnnotation.class)); + + assertThat(entity.getTableName()).isEqualTo(CqlIdentifier.cqlId("mytable", true)); + } + + /** + * @see DATACASS-259 + */ + @Test + public void shouldConsiderComposedPrimaryKeyClassAnnotation() { + + BasicCassandraPersistentEntity entity = new BasicCassandraPersistentEntity( + ClassTypeInformation.from(PrimaryKeyClassWithComposedAnnotation.class)); + + assertThat(entity.isCompositePrimaryKey()).isTrue(); + } + @Table("messages") static class Message {} @@ -132,4 +162,25 @@ public class BasicCassandraPersistentEntityUnitTests { return tableName; } } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE }) + @Table(forceQuote = true) + @interface ComposedTableAnnotation { + + @AliasFor(annotation = Table.class) + String value() default "mytable"; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE }) + @PrimaryKeyClass + @interface ComposedPrimaryKeyClass { + } + + @ComposedTableAnnotation() + static class TableWithComposedAnnotation {} + + @ComposedPrimaryKeyClass() + static class PrimaryKeyClassWithComposedAnnotation {} } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentPropertyUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentPropertyUnitTests.java index 76d045602..2e4d8de65 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentPropertyUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentPropertyUnitTests.java @@ -17,21 +17,103 @@ package org.springframework.data.cassandra.mapping; import static org.assertj.core.api.Assertions.*; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; import java.util.Date; -import org.junit.Before; import org.junit.Test; +import org.springframework.cassandra.core.cql.CqlIdentifier; +import org.springframework.core.annotation.AliasFor; import org.springframework.data.util.ClassTypeInformation; import org.springframework.util.ReflectionUtils; +import com.datastax.driver.core.DataType.Name; + /** * Unit tests for {@link BasicCassandraPersistentProperty}. * * @author Alex Shvid + * @author Mark Paluch */ public class BasicCassandraPersistentPropertyUnitTests { + @Test + public void usesAnnotatedColumnName() { + assertThat(getPropertyFor(Timeline.class, "text").getColumnName().toCql()).isEqualTo("message"); + } + + @Test + public void checksIdProperty() { + + CassandraPersistentProperty property = getPropertyFor(Timeline.class, "id"); + + assertThat(property.isIdProperty()).isTrue(); + } + + @Test + public void returnsPropertyNameForUnannotatedProperty() { + assertThat(getPropertyFor(Timeline.class, "time").getColumnName().toCql()).isEqualTo("time"); + } + + /** + * @see DATACASS-259 + */ + @Test + public void shouldConsiderComposedColumnAnnotation() { + + CassandraPersistentProperty persistentProperty = getPropertyFor(TypeWithComposedColumnAnnotation.class, "column"); + assertThat(persistentProperty.getColumnName()).isEqualTo(CqlIdentifier.cqlId("mycolumn", true)); + } + + /** + * @see DATACASS-259 + */ + @Test + public void shouldConsiderComposedPrimaryKeyColumnAnnotation() { + + CassandraPersistentProperty persistentProperty = getPropertyFor(TypeWithComposedPrimaryKeyColumnAnnotation.class, + "column"); + assertThat(persistentProperty.getColumnName()).isEqualTo(CqlIdentifier.cqlId("mycolumn", true)); + assertThat(persistentProperty.isPrimaryKeyColumn()).isTrue(); + } + + /** + * @see DATACASS-259 + */ + @Test + public void shouldConsiderComposedPrimaryKeyAnnotation() { + + CassandraPersistentProperty persistentProperty = getPropertyFor(TypeWithComposedPrimaryKeyAnnotation.class, + "column"); + assertThat(persistentProperty.getColumnName()).isEqualTo(CqlIdentifier.cqlId("primary-key", true)); + assertThat(persistentProperty.isIdProperty()).isTrue(); + } + + /** + * @see DATACASS-259 + */ + @Test + public void shouldConsiderComposedCassandraTypeAnnotation() { + + CassandraPersistentProperty persistentProperty = getPropertyFor(TypeWithComposedCassandraTypeAnnotation.class, + "column"); + + assertThat(persistentProperty.getDataType().getName()).isEqualTo(Name.COUNTER); + assertThat(persistentProperty.findAnnotation(CassandraType.class)).isNotNull(); + } + + private CassandraPersistentProperty getPropertyFor(Class type, String fieldName) { + + Field field = ReflectionUtils.findField(type, fieldName); + + return new BasicCassandraPersistentProperty(field, null, getEntity(type), new CassandraSimpleTypeHolder()); + } + + private BasicCassandraPersistentEntity getEntity(Class type) { + return new BasicCassandraPersistentEntity(ClassTypeInformation.from(type)); + } + static class Timeline { @PrimaryKey String id; @@ -39,37 +121,53 @@ public class BasicCassandraPersistentPropertyUnitTests { Date time; @Column("message") String text; - } - CassandraPersistentEntity entity; + @Retention(RetentionPolicy.RUNTIME) + @Column(forceQuote = true) + @interface ComposedColumnAnnotation { - @Before - public void setup() { - entity = new BasicCassandraPersistentEntity(ClassTypeInformation.from(Timeline.class)); + @AliasFor(annotation = Column.class) + String value(); } - @Test - public void usesAnnotatedColumnName() { + @Retention(RetentionPolicy.RUNTIME) + @PrimaryKeyColumn(forceQuote = true) + @interface ComposedPrimaryKeyColumnAnnotation { - Field field = ReflectionUtils.findField(Timeline.class, "text"); - assertThat(getPropertyFor(field).getColumnName().toCql()).isEqualTo("message"); + @AliasFor(annotation = PrimaryKeyColumn.class) + String value(); + + @AliasFor(annotation = PrimaryKeyColumn.class) + int ordinal() default 42; } - @Test - public void checksIdProperty() { - Field field = ReflectionUtils.findField(Timeline.class, "id"); - CassandraPersistentProperty property = getPropertyFor(field); - assertThat(property.isIdProperty()).isTrue(); + @Retention(RetentionPolicy.RUNTIME) + @PrimaryKey(forceQuote = true) + @interface ComposedPrimaryKeyAnnotation { + + @AliasFor(annotation = PrimaryKey.class) + String value() default "primary-key"; } - @Test - public void returnsPropertyNameForUnannotatedProperty() { - Field field = ReflectionUtils.findField(Timeline.class, "time"); - assertThat(getPropertyFor(field).getColumnName().toCql()).isEqualTo("time"); + @Retention(RetentionPolicy.RUNTIME) + @CassandraType(type = Name.COUNTER) + @interface ComposedCassandraTypeAnnotation { } - private CassandraPersistentProperty getPropertyFor(Field field) { - return new BasicCassandraPersistentProperty(field, null, entity, new CassandraSimpleTypeHolder()); + static class TypeWithComposedColumnAnnotation { + @ComposedColumnAnnotation("mycolumn") String column; + } + + static class TypeWithComposedPrimaryKeyColumnAnnotation { + @ComposedPrimaryKeyColumnAnnotation("mycolumn") String column; + } + + static class TypeWithComposedPrimaryKeyAnnotation { + @ComposedPrimaryKeyAnnotation String column; + } + + static class TypeWithComposedCassandraTypeAnnotation { + @ComposedCassandraTypeAnnotation String column; } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/CassandraUserTypePersistentEntityUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/CassandraUserTypePersistentEntityUnitTests.java index 648ae8cf5..28b508cdb 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/CassandraUserTypePersistentEntityUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/CassandraUserTypePersistentEntityUnitTests.java @@ -17,11 +17,17 @@ package org.springframework.data.cassandra.mapping; import static org.assertj.core.api.Assertions.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + 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.core.annotation.AliasFor; import org.springframework.data.util.ClassTypeInformation; /** @@ -82,6 +88,17 @@ public class CassandraUserTypePersistentEntityUnitTests { assertThat(type.getTableName()).isEqualTo(CqlIdentifier.cqlId("UpperCase", true)); } + /** + * @see DATACASS-259 + */ + @Test + public void shouldConsiderComposedUserDefinedTypeAnnotation() { + + CassandraUserTypePersistentEntity type = getEntity(TypeWithComposedAnnotation.class); + + assertThat(type.getTableName()).isEqualTo(CqlIdentifier.cqlId("mytype", true)); + } + private CassandraUserTypePersistentEntity getEntity(Class entityClass) { return new CassandraUserTypePersistentEntity(ClassTypeInformation.from(entityClass), mappingContextMock, null, userTypeResolverMock); @@ -95,4 +112,16 @@ public class CassandraUserTypePersistentEntityUnitTests { @UserDefinedType(value = "UpperCase", forceQuote = true) static class WithForceQuote {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE }) + @UserDefinedType(forceQuote = true) + @interface ComposedUserDefinedTypeAnnotation { + + @AliasFor(annotation = UserDefinedType.class) + String value() default "mytype"; + } + + @ComposedUserDefinedTypeAnnotation() + static class TypeWithComposedAnnotation {} } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/CassandraParametersUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/CassandraParametersUnitTests.java index 94ce8dcfd..c9d4a1124 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/CassandraParametersUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/CassandraParametersUnitTests.java @@ -17,6 +17,8 @@ package org.springframework.data.cassandra.repository.query; import static org.assertj.core.api.Assertions.*; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import org.junit.Test; @@ -86,6 +88,18 @@ public class CassandraParametersUnitTests { assertThat(cassandraParameters.getParameter(0).getCassandraType().type()).isEqualTo(Name.TIME); } + /** + * @see DATACASS-296 + */ + @Test + public void shouldReturnTypeForComposedAnnotationType() throws Exception { + + Method method = PersonRepository.class.getMethod("findByComposedAnnotationObject", Object.class); + CassandraParameters cassandraParameters = new CassandraParameters(method); + + assertThat(cassandraParameters.getParameter(0).getCassandraType().type()).isEqualTo(Name.BOOLEAN); + } + interface PersonRepository { Person findByFirstname(String firstname); @@ -95,5 +109,12 @@ public class CassandraParametersUnitTests { Person findByObject(Object firstname); Person findByAnnotatedObject(@CassandraType(type = Name.TIME) Object firstname); + + Person findByComposedAnnotationObject(@ComposedCassandraTypeAnnotation Object firstname); + } + + @Retention(RetentionPolicy.RUNTIME) + @CassandraType(type = Name.BOOLEAN) + @interface ComposedCassandraTypeAnnotation { } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQueryUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQueryUnitTests.java index 3559c2f2a..adde08180 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQueryUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQueryUnitTests.java @@ -18,6 +18,8 @@ package org.springframework.data.cassandra.repository.query; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.nio.ByteBuffer; @@ -125,6 +127,21 @@ public class StringBasedCassandraQueryUnitTests { assertThat(actual).isEqualTo("SELECT * FROM person WHERE lastname = 'Matthews';"); } + /** + * @see DATACASS-259 + */ + @Test + public void bindsIndexParameterForComposedQueryAnnotationCorrectly() { + + StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByComposedQueryAnnotation", String.class); + CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor( + cassandraQuery.getQueryMethod(), "Matthews"); + + String actual = cassandraQuery.createQuery(accessor); + + assertThat(actual).isEqualTo("SELECT * FROM person WHERE lastname = 'Matthews';"); + } + /** * @see DATACASS-117 */ @@ -462,5 +479,13 @@ public class StringBasedCassandraQueryUnitTests { @Query("SELECT * FROM person WHERE address=?0;") Person findByMainAddress(UDTValue udtValue); + + @ComposedQueryAnnotation + Person findByComposedQueryAnnotation(String lastname); + } + + @Retention(RetentionPolicy.RUNTIME) + @Query("SELECT * FROM person WHERE lastname = ?0;") + @interface ComposedQueryAnnotation { } }