DATACASS-360 - Do not require @Table annotation.

We no longer require entities to be annotated with @Table for data mapping and CRUD operations.
Entities without @Table can be still mapped in both directions and will be excluded from schema-management to prevent table creation for unwanted classes.

A class annotated with @Table will participate in schema management and be exposed as a table entity. Classes used as entities without @Table can still be used to query Cassandra but schema management will not create any tables for these classes.

@Table // entity qualified for schema management
class Person {
  @Id private String id;
  private String lastname;
  private String firstname;
}

// entity that can be used for
// select/insert/update/delete operations and repository use
class Person {
  @Id private String id;
  private String lastname;
  private String firstname;
}
This commit is contained in:
Mark Paluch
2016-11-28 16:50:47 +01:00
committed by John Blum
parent ff69fb2423
commit 996275188c
8 changed files with 42 additions and 69 deletions

View File

@@ -25,6 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.UserType;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.cql.generator.CreateUserTypeCqlGenerator;
@@ -36,10 +40,6 @@ import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.util.Assert;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.UserType;
/**
* Schema creation support for Cassandra based on {@link CassandraMappingContext} and {@link CassandraPersistentEntity}.
* This class generates CQL to drop, recreate and create user types (UDT) and tables.
@@ -149,7 +149,7 @@ public class CassandraPersistentEntitySchemaCreator {
protected List<CreateTableSpecification> createTableSpecifications(boolean ifNotExists) {
Collection<? extends CassandraPersistentEntity<?>> entities = new ArrayList<>(
mappingContext.getNonPrimaryKeyEntities());
mappingContext.getTableEntities());
List<CreateTableSpecification> specifications = new ArrayList<>();

View File

@@ -79,9 +79,9 @@ public class BasicCassandraMappingContext
protected Map<Class<?>, CassandraPersistentEntity<?>> entitiesByType = new HashMap<Class<?>, CassandraPersistentEntity<?>>();
protected Map<CqlIdentifier, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<CqlIdentifier, Set<CassandraPersistentEntity<?>>>();
protected Set<CassandraPersistentEntity<?>> nonPrimaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Set<CassandraPersistentEntity<?>> primaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Set<CassandraPersistentEntity<?>> userDefinedTypes = new HashSet<CassandraPersistentEntity<?>>();
protected Set<CassandraPersistentEntity<?>> tableEntities = new HashSet<CassandraPersistentEntity<?>>();
private CustomConversions customConversions;
@@ -128,8 +128,8 @@ public class BasicCassandraMappingContext
}
@Override
public Collection<CassandraPersistentEntity<?>> getPersistentEntities() {
return getPersistentEntities(false);
public Collection<CassandraPersistentEntity<?>> getTableEntities() {
return Collections.unmodifiableCollection(tableEntities);
}
@Override
@@ -139,7 +139,7 @@ public class BasicCassandraMappingContext
@Override
public Collection<CassandraPersistentEntity<?>> getNonPrimaryKeyEntities() {
return Collections.unmodifiableSet(nonPrimaryKeyEntities);
return getTableEntities();
}
@Override
@@ -158,7 +158,7 @@ public class BasicCassandraMappingContext
return super.getPersistentEntities();
}
return Collections.unmodifiableSet(nonPrimaryKeyEntities);
return getTableEntities();
}
@Override
@@ -207,10 +207,10 @@ public class BasicCassandraMappingContext
if (!entity.isUserDefinedType()) {
if (entity.isCompositePrimaryKey()) {
primaryKeyEntities.add(entity);
} else {
if (entity.findAnnotation(Persistent.class) != null) {
nonPrimaryKeyEntities.add(entity);
}
}
if (entity.findAnnotation(Table.class) != null) {
tableEntities.add(entity);
}
}

View File

@@ -43,6 +43,13 @@ public interface CassandraMappingContext
@Override
Collection<CassandraPersistentEntity<?>> getPersistentEntities();
/**
* Returns only {@link Table} entities.
*
* @since 1.5
*/
Collection<CassandraPersistentEntity<?>> getTableEntities();
/**
* Returns all persistent entities or only non-primary-key entities.
*
@@ -53,14 +60,18 @@ public interface CassandraMappingContext
/**
* Returns only those entities representing primary key types.
* @deprecated as of 1.5
*/
@Deprecated
Collection<CassandraPersistentEntity<?>> getPrimaryKeyEntities();
/**
* Returns only those entities not representing primary key types.
*
* @see #getPersistentEntities(boolean)
* @deprecated as of 1.5, use {@link #getTableEntities()}.
*/
@Deprecated
Collection<CassandraPersistentEntity<?>> getNonPrimaryKeyEntities();
/**

View File

@@ -17,9 +17,7 @@ package org.springframework.data.cassandra.mapping;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.util.Assert;
@@ -43,8 +41,8 @@ public class CompositeCassandraPersistentEntityMetadataVerifier implements Cassa
* @see PrimaryKeyClassEntityMetadataVerifier
*/
public CompositeCassandraPersistentEntityMetadataVerifier() {
this(Arrays.asList(new PersistentAnnotationVerifier(), new PrimaryKeyClassEntityMetadataVerifier(),
new BasicCassandraPersistentEntityMetadataVerifier()));
this(Arrays.asList(new PrimaryKeyClassEntityMetadataVerifier(),
new BasicCassandraPersistentEntityMetadataVerifier()));
}
/**
@@ -70,30 +68,4 @@ public class CompositeCassandraPersistentEntityMetadataVerifier implements Cassa
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, @UserDefinedType or a @PrimaryKey
if (entity.findAnnotation(Persistent.class) == null && entity.findAnnotation(UserDefinedType.class) == null) {
throw new VerifierMappingExceptions(entity,
Collections.singletonList(new MappingException(
String.format("Cassandra entities must be annotated with either @%s, @%s, @%s or @%s",
Persistent.class.getSimpleName(), Table.class.getSimpleName(),
UserDefinedType.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName()))));
}
}
}
}

View File

@@ -66,7 +66,7 @@ public class BasicCassandraMappingContextUnitTests {
});
}
@Test(expected = MappingException.class)
@Test
public void testGetPersistentEntityOfTransientType() {
mappingContext.getPersistentEntity(Transient.class);
}
@@ -392,11 +392,11 @@ public class BasicCassandraMappingContextUnitTests {
CassandraPersistentEntity<?> existingPersistentEntity = mappingContext.getPersistentEntity(MappedUdt.class);
assertThat(mappingContext.getNonPrimaryKeyEntities()).doesNotContain(existingPersistentEntity);
assertThat(mappingContext.getTableEntities()).doesNotContain(existingPersistentEntity);
}
/**
* @see DATACASS-172
* @see DATACASS-172, DATACASS-359
*/
@Test
public void getPersistentEntitiesShouldContainUdt() {
@@ -404,7 +404,9 @@ public class BasicCassandraMappingContextUnitTests {
CassandraPersistentEntity<?> existingPersistentEntity = mappingContext.getPersistentEntity(MappedUdt.class);
assertThat(mappingContext.getPersistentEntities(true)).contains(existingPersistentEntity);
assertThat(mappingContext.getUserDefinedTypeEntities()).contains(existingPersistentEntity);
assertThat(mappingContext.getPersistentEntities(false)).doesNotContain(existingPersistentEntity);
assertThat(mappingContext.getTableEntities()).doesNotContain(existingPersistentEntity);
}
/**

View File

@@ -65,18 +65,11 @@ public class CompositeCassandraPersistentEntityMetadataVerifierUnitTests {
}
/**
* @see DATACASS-258
* @see DATACASS-258, DATACASS-359
*/
@Test
public void shouldFailWithNonPersistentClasses() {
try {
verifier.verify(getEntity(NonPersistentClass.class));
fail("Missing MappingException");
} catch (MappingException e) {
assertThat(e).hasMessageContaining(
"Cassandra entities must be annotated with either @Persistent, @Table, @UserDefinedType or @PrimaryKeyClass");
}
public void shouldNotFailWithNonPersistentClasses() {
verifier.verify(getEntity(NonPersistentClass.class));
}
/**

View File

@@ -17,13 +17,15 @@ package org.springframework.data.cassandra.test.integration.repository.cdi;
import java.util.Collections;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.inject.Singleton;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import com.datastax.driver.core.Cluster;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Service;
import org.springframework.cassandra.core.cql.generator.CreateKeyspaceCqlGenerator;
import org.springframework.cassandra.core.cql.generator.DropKeyspaceCqlGenerator;
import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification;
@@ -39,10 +41,6 @@ import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.SimpleUserTypeResolver;
import org.springframework.data.cassandra.test.integration.repository.simple.User;
import com.datastax.driver.core.Cluster;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Service;
/**
* @author Mark Paluch
*/
@@ -55,9 +53,8 @@ class CassandraOperationsProducer {
public Cluster createCluster() throws Exception {
CassandraConnectionProperties properties = new CassandraConnectionProperties();
Cluster cluster = Cluster.builder().addContactPoint(properties.getCassandraHost())
return Cluster.builder().addContactPoint(properties.getCassandraHost())
.withPort(properties.getCassandraPort()).build();
return cluster;
}
@Produces
@@ -83,7 +80,7 @@ class CassandraOperationsProducer {
schemaCreator.createTables(false, false, true);
for (CassandraPersistentEntity<?> entity : cassandraTemplate.getConverter().getMappingContext()
.getPersistentEntities()) {
.getTableEntities()) {
cassandraTemplate.truncate(entity.getType());
}
@@ -112,5 +109,4 @@ class CassandraOperationsProducer {
public Set<Service> producerToSatisfyGuavaDependenciesWhenTesting() {
return Sets.newHashSet();
}
}

View File

@@ -39,8 +39,7 @@ public abstract class AbstractSpringDataEmbeddedCassandraIntegrationTest
*/
public void deleteAllEntities() {
for (CassandraPersistentEntity<?> entity : template.getConverter().getMappingContext().getPersistentEntities()) {
for (CassandraPersistentEntity<?> entity : template.getConverter().getMappingContext().getTableEntities()) {
if (entity.getType().isInterface()) {
continue;
}