Introdução ao Spring Data Cassandra

Introdução ao Spring Data Cassandra

1. Visão geral

Este artigo é uma introdução prática ao trabalho com Cassandra com Spring Data.

Começaremos o básico e passaremos pelas configurações e codificação, finalmente construindo um módulo completo do Spring Data Cassandra.

2. Dependências do Maven

Vamos começar definindo as dependências empom.xml, com Maven:


    com.datastax.cassandra
    cassandra-driver-core
    2.1.9

3. Configuração para Cassandra

Usaremos o estilo de configuração Java em todo este processo para configurar a integração do Cassandra.

3.1. A configuração principal

Vamos começar com a classe de configuração principal - claro, conduzida por meio da anotação de nível de classe@Configuration :

@Configuration
public class CassandraConfig extends AbstractCassandraConfiguration {

    @Override
    protected String getKeyspaceName() {
        return "testKeySpace";
    }

    @Bean
    public CassandraClusterFactoryBean cluster() {
        CassandraClusterFactoryBean cluster =
          new CassandraClusterFactoryBean();
        cluster.setContactPoints("127.0.0.1");
        cluster.setPort(9142);
        return cluster;
    }

    @Bean
    public CassandraMappingContext cassandraMapping()
      throws ClassNotFoundException {
        return new BasicCassandraMappingContext();
    }
}

Observe o novo bean -BasicCassandraMappingContext - com uma implementação padrão. Isso é necessário para mapear as entidades persistentes entre seus objetos e seus formatos persistentes.

E como a implementação padrão é capaz o suficiente, podemos usá-la diretamente.

3.2. Propriedades de conexão Cassandra

Existem três configurações obrigatórias que precisamos definir para configurar a conexão para um cliente Cassandra.

Temos que configurar o nome do host para que o servidor Cassandra rodando como contactPoints. Port seja simplesmente a porta de escuta para solicitação no servidor. KeyspaceName é o namespace que define a replicação de dados em nós, que é baseado em um conceito relacionado ao Cassandra.

4. O Repositório Cassandra

Vamos usar umCassandraRepository para a camada de acesso a dados. Isso segue a abstração do repositório Spring Data, focada em abstrair o código necessário para implementar as camadas de acesso a dados em diferentes mecanismos de persistência.

4.1. Crie oCassandraRepository

Vamos criar oCassandraRepository para ser usado na configuração:

@Repository
public interface BookRepository extends CassandraRepository {
    //
}

4.2. Configuração paraCassandraRepository

Agora, podemos estender a configuração na Seção 3.1, adicionando a anotação de nível de classe@EnableCassandraRepositories para marcar nosso Repositório Cassandra criado na seção 4.1 emCassandraConfig:

@Configuration
@EnableCassandraRepositories(
  basePackages = "org.example.spring.data.cassandra.repository")
public class CassandraConfig extends AbstractCassandraConfiguration {
    //
}

5. A entidade

Vamos dar uma olhada rápida na entidade - a classe de modelo que vamos usar. A classe é anotada e define parâmetros adicionais para a criação da tabela de dados do Cassandra de metadados no modo incorporado.

Usando a anotação@Table, o bean é diretamente mapeado para uma tabela de dados do Cassandra. Além disso, cada propriedade é definida como um tipo de chave primária ou uma coluna simples:

@Table
public class Book {
    @PrimaryKeyColumn(
      name = "isbn",
      ordinal = 2,
      type = PrimaryKeyType.CLUSTERED,
      ordering = Ordering.DESCENDING)
    private UUID id;
    @PrimaryKeyColumn(
      name = "title", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
    private String title;
    @PrimaryKeyColumn(
      name = "publisher", ordinal = 1, type = PrimaryKeyType.PARTITIONED)
    private String publisher;
    @Column
    private Set tags = new HashSet<>();
    // standard getters and setters
}

6. Testando com um servidor integrado

6.1. Dependências do Maven

Se você deseja executar o Cassandra no modo incorporado (sem instalar manualmente um servidor Cassandra separado), é necessário adicionar as dependências relacionadascassandra-unit aopom.xml:


    org.cassandraunit
    cassandra-unit-spring
    2.1.9.2
    test
    
        
        org.cassandraunit
        cassandra-unit
        
    


    org.cassandraunit
    cassandra-unit-shaded
    2.1.9.2
    test


    org.hectorclient
    hector-core
    2.0-0

É possíveluse an embedded Cassandra server to test this application. A principal vantagem é que você não deseja instalar o Cassandra explicitamente.

Este servidor incorporado também é compatível com os testes Spring JUnit. Aqui podemos definirSpringJUnit4ClassRunner usando a anotação@RunWith junto com o servidor embutido. Portanto, é possível implementar um conjunto de testes completo sem a execução de um serviço Cassandra externo.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CassandraConfig.class)
public class BookRepositoryIntegrationTest {
    //
}

6.2. Iniciando e parando o servidor

Você pode ignorar esta seção se estiver executando um servidor Cassandra externo.

Temos que iniciar o servidor uma vez para todo o conjunto de testes, então o método de início do servidor é marcado com a anotação@BeforeClass:

@BeforeClass
public static void startCassandraEmbedded() {
    EmbeddedCassandraServerHelper.startEmbeddedCassandra();
    Cluster cluster = Cluster.builder()
      .addContactPoints("127.0.0.1").withPort(9142).build();
    Session session = cluster.connect();
}

Em seguida, precisamos garantir que o servidor seja parado após a conclusão da execução do conjunto de testes:

@AfterClass
public static void stopCassandraEmbedded() {
    EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}

6.3. Limpar Tabela de Dados

É uma boa prática descartar e criar a tabela de dados antes de cada execução de teste, para evitar resultados inesperados devido aos dados manipulados nas execuções de teste anteriores.

Agora podemos criar a tabela de dados quando o servidor é iniciado:

@Before
public void createTable() {
    adminTemplate.createTable(
      true, CqlIdentifier.cqlId(DATA_TABLE_NAME),
      Book.class, new HashMap());
}

e solte após cada execução de caso de teste:

@After
public void dropTable() {
    adminTemplate.dropTable(CqlIdentifier.cqlId(DATA_TABLE_NAME));
}

7. Acesso a dados usandoCassandraRepository

Podemos usar diretamente osBookRepository que criamos acima para persistir, manipular e buscar os dados no banco de dados do Cassandra.

7.1. Salvar um novo livro

Podemos salvar um novo livro em nossa livraria:

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media",
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));

Em seguida, podemos verificar a disponibilidade do livro inserido no banco de dados:

Iterable books = bookRepository.findByTitleAndPublisher(
  "Head First Java", "O'Reilly Media");
assertEquals(javaBook.getId(), books.iterator().next().getId());

7.2. Atualizar um livro existente

Lat's começa inserindo um novo livro:

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media",
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));

Vamos buscar o livro pelo título:

Iterable books = bookRepository.findByTitleAndPublisher(
  "Head First Java", "O'Reilly Media");

Então vamos mudar o título do livro:

javaBook.setTitle("Head First Java Second Edition");
bookRepository.save(ImmutableSet.of(javaBook));

Por fim, vamos verificar se o título está atualizado no banco de dados:

Iterable books = bookRepository.findByTitleAndPublisher(
  "Head First Java Second Edition", "O'Reilly Media");
assertEquals(
  javaBook.getTitle(), updateBooks.iterator().next().getTitle());

7.3. Exclua o livro existente

Insira um novo livro:

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media",
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));

Em seguida, exclua o livro recém-inserido:

bookRepository.delete(javaBook);

Agora podemos verificar a exclusão:

Iterable books = bookRepository.findByTitleAndPublisher(
  "Head First Java", "O'Reilly Media");
assertNotEquals(javaBook.getId(), books.iterator().next().getId());

Isso fará com que seja lançada uma NoSuchElementException do código, garantindo que o livro seja excluído.

7.4. Encontre todos os livros

Insira um novo livro primeiro:

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media",
  ImmutableSet.of("Computer", "Software"));
Book dPatternBook = new Book(
  UUIDs.timeBased(), "Head Design Patterns","O'Reilly Media",
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));
bookRepository.save(ImmutableSet.of(dPatternBook));

Encontre todos os livros:

Iterable books = bookRepository.findAll();

Depois, podemos verificar o número de livros disponíveis no banco de dados:

int bookCount = 0;
for (Book book : books) bookCount++;
assertEquals(bookCount, 2);

8. Conclusão

Passamos por uma introdução prática básica ao Cassandra com dados Spring usando a abordagem mais comum usando o mecanismo de acesso a dadosCassandraRepository.

A implementação dos trechos de código e exemplos acima podem ser encontrados emmy GitHub project - este é um projeto baseado em Eclipse, portanto, deve ser fácil de importar e executar como está.