In this article, we will see how we can have our own cassandra configuration rather than letting spring rely on cassandra auto-configuration mechanism.
Cassandra Configuration
First thing to be done is to extend spring’s AbstractCassandraConfiguration
. In order to externalize the configuration, we need to provide the CassandraProperties
and override the getter properties so that the properties set in application.properties comes in use to build the cluster.
All cassandra properties defined in application.properties gets encapsulated into CassandraProperties
.
The cassandra properties start with prefix “spring.data.cassandra”.
SimpleCassandraConfig :
/* * Copyright (c) 2019 Navis LLC. All Rights Reserved. */ package com.javarticles.cassandra.demo.config; import com.javarticles.cassandra.demo.domain.Movie; import org.apache.tomcat.util.buf.StringUtils; import org.springframework.boot.autoconfigure.cassandra.CassandraProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.data.cassandra.config.AbstractCassandraConfiguration; import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification.createKeyspace; /** * Application configuration */ @Configuration @EnableConfigurationProperties(CassandraProperties.class) @ConfigurationProperties("spring.data.cassandra") public class SimpleCassandraConfig extends AbstractCassandraConfiguration { private final CassandraProperties properties; private int replicationFactor; public SimpleCassandraConfig(CassandraProperties properties) { this.properties = properties; } @Override protected String getKeyspaceName() { return properties.getKeyspaceName(); } public boolean getMetricsEnabled() { return false; } public String[] getEntityBasePackages() { ListentityPackages = new ArrayList<>(); entityPackages.add(Movie.class.getPackage().getName()); return entityPackages.toArray(new String[0]); } public SchemaAction getSchemaAction() { return SchemaAction.valueOf(properties.getSchemaAction().toUpperCase()); } public String getContactPoints() { return StringUtils.join(properties.getContactPoints()); } @Override public int getPort() { return properties.getPort(); } public Integer getReplicationFactor() { return replicationFactor; } public void setReplicationFactor(int replicationFactor) { this.replicationFactor = replicationFactor; } @Override protected List getKeyspaceCreations() { return Collections.singletonList(createKeyspace(getKeyspaceName()).ifNotExists().withSimpleReplication(getReplicationFactor())); } @Override protected String getClusterName() { return properties.getClusterName(); } }
Here we override getKeyspaceCreations()
to create the keyspace if doesn’t exist.
Now we will test the cassandra integration with a simple domain class called Movie
.
Movie:
package com.javarticles.cassandra.demo.domain; import org.springframework.data.cassandra.core.cql.PrimaryKeyType; import org.springframework.data.cassandra.core.mapping.Column; import org.springframework.data.cassandra.core.mapping.PrimaryKey; import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; import org.springframework.data.cassandra.core.mapping.Table; import java.util.Map; @Table("movie") public class Movie { @PrimaryKey private String id; @Column private String name; @Column private String genre; public Movie(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGenre() { return genre; } public void setGenre(String genre) { this.genre = genre; } }
Here is the movie repository.
MovieRepository:
package com.javarticles.cassandra.demo.domain; import org.springframework.data.cassandra.repository.CassandraRepository; import org.springframework.stereotype.Repository; @Repository public interface MovieRepository extends CassandraRepository{ }
We specify the cassandra properties.
application.properties:
server.port=8081 spring.data.cassandra.keyspace-name=movies spring.data.cassandra.schema-action=recreate spring.data.cassandra.contactpoints=localhost spring.data.cassandra.port=9042 logging.level.org.springframework.data.cassandra.core.cql: DEBUG
Now we will put this into test.
CassandraExamplesApplicationTests:
package com.javarticles.cassandra.demo; import com.javarticles.cassandra.demo.domain.Movie; import com.javarticles.cassandra.demo.domain.MovieRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import static junit.framework.TestCase.assertNotNull; import static org.junit.jupiter.api.Assertions.assertEquals; @SpringBootTest class CassandraExamplesApplicationTests { @Autowired private MovieRepository movieRepository; @Test void movieCreation() { Movie movie = new Movie("01", "Batman"); movie.setGenre("Superhero"); movieRepository.save(movie); movie = movieRepository.findById("01").orElse(null); assertNotNull(movie); assertEquals("Batman", movie.getName()); } }
Output:
2020-08-05 19:12:14.610 INFO 17536 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 21ms. Found 1 Cassandra repository interfaces. 2020-08-05 19:12:15.371 INFO 17536 --- [ main] com.datastax.driver.core : DataStax Java driver 3.7.2 for Apache Cassandra .... 2020-08-05 19:12:16.675 WARN 17536 --- [ main] com.datastax.driver.core.Cluster : You listed localhost/0:0:0:0:0:0:0:1:9042 in your contact points, but it wasn't found in the control host's system.peers at startup 2020-08-05 19:12:16.848 INFO 17536 --- [ main] c.d.d.c.p.DCAwareRoundRobinPolicy : Using data-center name 'datacenter1' for DCAwareRoundRobinPolicy (if this is incorrect, please provide the correct datacenter name with DCAwareRoundRobinPolicy constructor) 2020-08-05 19:12:16.850 INFO 17536 --- [ main] com.datastax.driver.core.Cluster : New Cassandra host localhost/127.0.0.1:9042 added 2020-08-05 19:12:16.898 DEBUG 17536 --- [ main] o.s.data.cassandra.core.cql.CqlTemplate : Executing CQL Statement [CREATE KEYSPACE IF NOT EXISTS movies WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 0 } AND durable_writes = true;] 2020-08-05 19:12:17.165 DEBUG 17536 --- [ main] o.s.data.cassandra.core.cql.CqlTemplate : Executing CQL Statement [DROP TABLE movie;] 2020-08-05 19:12:18.300 DEBUG 17536 --- [ main] o.s.data.cassandra.core.cql.CqlTemplate : Executing CQL Statement [CREATE TABLE movie (genre text, id text, name text, PRIMARY KEY (id));] ... 2020-08-05 19:12:20.418 DEBUG 17536 --- [ main] o.s.data.cassandra.core.cql.CqlTemplate : Executing CQL Statement [SELECT * FROM movie WHERE id='01';]