Introduction à Spring Data Cassandra

Introduction à Spring Data Cassandra

1. Vue d'ensemble

Cet article est une introduction pratique à l’utilisation de Cassandra avec Spring Data.

Nous allons commencer par les bases et passer en revue les configurations et le codage, pour finalement créer un module complet Spring Data Cassandra.

2. Dépendances Maven

Commençons par définir les dépendances dans lespom.xml, avec Maven:


    com.datastax.cassandra
    cassandra-driver-core
    2.1.9

3. Configuration pour Cassandra

Nous utiliserons le style de configuration Java tout au long de tout cela pour configurer l'intégration Cassandra.

3.1. La configuration principale

Commençons par la classe de configuration principale - bien sûr pilotée via la annotation de niveau 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();
    }
}

Notez le nouveau bean -BasicCassandraMappingContext - avec une implémentation par défaut. Cela est nécessaire pour mapper les entités persistantes entre leur objet et leurs formats persistants.

Et comme l'implémentation par défaut est suffisamment capable, nous pouvons l'utiliser directement.

3.2. Propriétés de connexion Cassandra

Il y a trois paramètres obligatoires à configurer pour configurer la connexion d'un client Cassandra.

Nous devons configurer le nom d'hôte que le serveur Cassandra exécutant en tant que contactPoints. Port est simplement le port d'écoute pour la demande dans le serveur. KeyspaceName est l'espace de noms qui définit la réplication des données sur les nœuds, qui est basée sur un concept lié à Cassandra.

4. Le référentiel Cassandra

Nous allons utiliser unCassandraRepository pour la couche d'accès aux données. Cela fait suite à l'abstraction du référentiel Spring Data, qui consiste à extraire le code requis pour implémenter les couches d'accès aux données à travers différents mécanismes de persistance.

4.1. Créer lesCassandraRepository

Créons lesCassandraRepository à utiliser dans la configuration:

@Repository
public interface BookRepository extends CassandraRepository {
    //
}

4.2. Configuration pourCassandraRepository

Maintenant, nous pouvons étendre la configuration de la section 3.1, en ajoutant l'annotation au niveau de la classe@EnableCassandraRepositories pour marquer notre référentiel Cassandra créé à la section 4.1 dansCassandraConfig:

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

5. L'entité

Jetons un coup d'œil à l'entité - la classe de modèle que nous allons utiliser. La classe est annotée et définit des paramètres supplémentaires pour la création de la table de données Cassandra des métadonnées en mode intégré.

En utilisant l'annotation@Table, le bean est directement mappé à une table de données Cassandra. De plus, chaque propriété est définie comme un type de clé primaire ou une colonne simple:

@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. Test avec un serveur intégré

6.1. Dépendances Maven

Si vous souhaitez exécuter Cassandra en mode intégré (sans installer manuellement un serveur Cassandra séparé), vous devez ajouter les dépendances liées àcassandra-unit auxpom.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

Il est possible deuse an embedded Cassandra server to test this application. Le principal avantage est que vous ne souhaitez pas installer Cassandra explicitement.

Ce serveur intégré est également compatible avec les tests Spring JUnit. Ici, nous pouvons définirSpringJUnit4ClassRunner en utilisant l'annotation@RunWith avec le serveur intégré. Il est donc possible d'implémenter une suite de tests complète sans faire fonctionner un service Cassandra externe.

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

6.2. Démarrage et arrêt du serveur

Vous pouvez ignorer cette section si vous utilisez un serveur Cassandra externe.

Nous devons démarrer le serveur une fois pour toute la suite de tests, donc la méthode de démarrage du serveur est marquée avec l'annotation@BeforeClass:

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

Ensuite, nous devons nous assurer que le serveur est arrêté après la fin de l'exécution de la suite de tests:

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

6.3. Nettoyer la table de données

Il est recommandé de supprimer et de créer la table de données avant chaque exécution de test afin d'éviter des résultats inattendus dus aux données manipulées lors d'exécutions de tests antérieures.

Nous pouvons maintenant créer la table de données au démarrage du serveur:

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

et abandonner après chaque exécution de test élémentaire:

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

7. Accès aux données à l'aide deCassandraRepository

Nous pouvons directement utiliser lesBookRepository que nous avons créés ci-dessus pour conserver, manipuler et récupérer les données dans la base de données Cassandra.

7.1. Enregistrer un nouveau livre

Nous pouvons enregistrer un nouveau livre dans notre librairie:

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

Ensuite, nous pouvons vérifier la disponibilité du livre inséré dans la base de données:

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

7.2. Mettre à jour un livre existant

Lat commence par insérer un nouveau livre:

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

Allons chercher le livre par le titre:

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

Modifions ensuite le titre du livre:

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

Enfin, vérifions si le titre est mis à jour dans la base de données:

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

7.3. Supprimer le livre existant

Insérer un nouveau livre:

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

Puis supprimez le nouveau livre entré:

bookRepository.delete(javaBook);

Nous pouvons maintenant vérifier la suppression:

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

Cela entraînera une exception NoSuchElementException du code en s'assurant que le livre est supprimé.

7.4. Trouver tous les livres

Insérez d'abord un nouveau livre:

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

Trouver tous les livres:

Iterable books = bookRepository.findAll();

Ensuite, nous pouvons vérifier le nombre de livres disponibles dans la base de données:

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

8. Conclusion

Nous avons effectué une introduction pratique de base à Cassandra avec les données Spring en utilisant l'approche la plus courante utilisant le mécanisme d'accès aux données deCassandraRepository.

L'implémentation des extraits de code et des exemples ci-dessus peut être trouvée dansmy GitHub project - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.