I am trying to connect to 2 different cassandra clusters using spring data cassandra. But it always uses only the first cassandra cluster config. The second one is not taking affect. Any idea what am I doing wrong? This is the config that I am using:
First cassandra cluster config:
#Configuration
#EnableCassandraRepositories(
basePackageClasses = SourceRepository.class
)
public class SourceCassandraConfig extends AbstractCassandraConfiguration {
#Override
public String getContactPoints() {
return "localhost";
}
#Override
public int getPort() {
return "9051";
}
#Override
protected String getKeyspaceName() {
return "source_keyspace";
}
}
Second cassandra cluster config:
#Configuration
#EnableCassandraRepositories(
basePackageClasses = TargetRepository.class,
cassandraTemplateRef = "targetCassandraTemplate"
)
public class TargetCassandraConfig extends AbstractCassandraConfiguration {
#Override
public String getContactPoints() {
return "localhost";
}
#Override
public int getPort() {
return "9052";
}
#Override
protected String getKeyspaceName() {
return "target_keyspace";
}
#Override
#Bean("targetSession")
public CassandraSessionFactoryBean session() throws ClassNotFoundException {
final CassandraSessionFactoryBean session = super.session();
session.setKeyspaceName(getKeyspaceName());
session.setCluster(cluster().getObject());
return session;
}
#Override
public CassandraCqlClusterFactoryBean cluster() {
CassandraCqlClusterFactoryBean cluster = super.cluster();
cluster.setContactPoints(contactPoints);
cluster.setPort(port);
return cluster;
}
#Bean("targetCassandraTemplate")
public CassandraAdminOperations cassandraTemplate(
#Qualifier("targetSession") final CassandraSessionFactoryBean session) throws Exception {
return new CassandraAdminTemplate(session.getObject(), cassandraConverter());
}
}
I always see that only the first cluster node is getting added
com.datastax.driver.core.Cluster : New Cassandra host localhost/127.0.0.1:9051 added
What am I doing wrong?
I spent 2 days debugging this and 10 mins after posting this question, I found the fix :)
I wasn't using the cluster bean that I created properly in the session bean. So I did the following and it worked:
#Override
#Bean("targetCassandraCluster")
public CassandraCqlClusterFactoryBean cluster() {
CassandraCqlClusterFactoryBean cluster = super.cluster();
cluster.setContactPoints(contactPoints);
cluster.setPort(port);
return cluster;
}
#Bean("targetCassandraSession")
public CassandraSessionFactoryBean session(
#Qualifier("targetCassandraCluster") final CassandraCqlClusterFactoryBean cluster
) throws ClassNotFoundException {
final CassandraSessionFactoryBean session = super.session();
session.setKeyspaceName(getKeyspaceName());
session.setCluster(cluster.getObject());
return session;
}
#Bean("targetCassandraTemplate")
public CassandraAdminOperations cassandraTemplate(
#Qualifier("targetCassandraSession") final CassandraSessionFactoryBean session) throws Exception {
return new CassandraAdminTemplate(session.getObject(), cassandraConverter());
}
Related
i have two keyspaces. one of them is created perfectly but the other one is only created with with SimpleReplication. When i create it with network replication it is created but when i do
describe keyspace in cqlsh it returns:
'NoneType' object has no attribute 'export_for_schema'
But the keyspace with network replication can be created from cqlsh.
In spring logs everything looks ok
[CREATE KEYSPACE IF NOT EXISTS a_events_local WITH replication = { 'class' : 'NetworkTopologyStrategy', 'dс1' : 3, 'dс2' : 3 }
My Cassandra Config
public abstract class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${cassandra.host}")
private String host;
#Value("${cassandra.port}")
private int port;
#Value("${cassandra.user}")
private String usr;
#Value("${cassandra.psswd}")
private String psswd;
#Value("${cassandra.keyspace.a}")
private String keyspaceA;
#Value("${cassandra.keyspace.b}")
private String keyspaceB;
#Override
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster =
new CassandraClusterFactoryBean();
cluster.setContactPoints(host);
cluster.setPort(port);
cluster.setUsername(usr);
cluster.setPassword(psswd);
cluster.setKeyspaceCreations(getKeyspaceCreations());
cluster.setJmxReportingEnabled(false);
return cluster;
}
#Override
protected String getContactPoints() {
return host;
}
#Override
protected int getPort() {
return port;
}
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.NONE;
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Arrays.asList(getAKeySpaceSpecification(), getBKeySpaceSpecification());
}
private CreateKeyspaceSpecification getAKeySpaceSpecification() {
return CreateKeyspaceSpecification.createKeyspace(keyspaceA)
.ifNotExists(true)
//.withSimpleReplication(3);
.withNetworkReplication(DataCenterReplication.of("dс1", 3) , DataCenterReplication.of("dс2", 3));
}
private CreateKeyspaceSpecification getBKeySpaceSpecification() {
return CreateKeyspaceSpecification.createKeyspace(keyspaceB)
.ifNotExists(true)
.withNetworkReplication(DataCenterReplication.of("dc1", 3), DataCenterReplication.of("dc2", 3));
}
Keyspace A config
#Configuration
#EnableCassandraRepositories(
cassandraTemplateRef = "keyspaceACassandraTemplate")
public class CassandraDDConfig extends CassandraConfig {
#Value("${cassandra.keyspace.a}")
private String keyspace;
#Value("${cassandra.a-entities-package}")
private String aEntityPackage;
#Override
#Bean("keyspaceDDSession")
public CassandraSessionFactoryBean session() {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setConverter(cassandraConverter());
session.setKeyspaceName(getKeyspaceName());
session.setSchemaAction(getSchemaAction());
session.setStartupScripts(getStartupScripts());
session.setShutdownScripts(getShutdownScripts());
return session;
}
#Override
#Bean("keyspaceACassandraTemplate")
public CassandraAdminTemplate cassandraTemplate() throws Exception {
return new CassandraAdminTemplate(this.session().getObject(), cassandraConverter());
}
#Override
protected String getKeyspaceName() {
return keyspace;
}
#Override
public String[] getEntityBasePackages() {
return new String[] {aEntityPackage};
}
}
Keyspace B config
#Configuration
#EnableCassandraRepositories(
cassandraTemplateRef = "keyspaceBCassandraTemplate")
public class CassandraSparkConfig extends CassandraConfig {
#Value("${cassandra.keyspace.b}")
private String keyspace;
#Value("${cassandra.b-entities-package}")
private String dcEntityPackage;
#Override
#Bean("keyspaceBSession")
public CassandraSessionFactoryBean session() {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setConverter(cassandraConverter());
session.setKeyspaceName(getKeyspaceName());
session.setSchemaAction(getSchemaAction());
session.setStartupScripts(getStartupScripts());
session.setShutdownScripts(getShutdownScripts());
return session;
}
#Override
#Bean("keyspaceSparkCassandraTemplate")
public CassandraAdminTemplate cassandraTemplate() throws Exception {
return new CassandraAdminTemplate(this.session().getObject(), cassandraConverter());
}
#Override
protected String getKeyspaceName() {
return keyspace;
}
#Override
public String[] getEntityBasePackages() {
String[] entities = new String[1];
if(saveAll){
entities[0] = bEntityPackage;
}
return entities;
}
}
in the end i used getStartupScripts to create keyspaces. and it works, data is written to and read from corresponding tables.
#Override
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster =
new CassandraClusterFactoryBean();
cluster.setContactPoints(host);
cluster.setPort(port);
cluster.setUsername(usr);
cluster.setPassword(psswd);
cluster.setStartupScripts(getStartupScripts());
cluster.setJmxReportingEnabled(false);
cluster.setMetricsEnabled(false);
#Override
protected List<String> getStartupScripts() {
final String scriptCreateAkeyspace =
"CREATE KEYSPACE IF NOT EXISTS "
+ keyspaceA
+ " WITH durable_writes = true"
+ " AND replication = {'class' : 'NetworkTopologyStrategy', 'dc1' : 3, 'dc2' : 1 };";
final String scriptCreateSparkKeyspace = "CREATE KEYSPACE IF NOT EXISTS "
+ keyspaceB
+ " WITH durable_writes = true"
+ " AND replication = {'class' : 'NetworkTopologyStrategy', 'dc1' : 1, 'dc2' : 3 };";
return Arrays.asList(scriptCreateAkeyspace, scriptCreateBKeyspace);
}
I used Spring 4.1.0 with Hibernate 4.3.6 and all is Ok.
After Spring migration to 4.2.8, the persistence does not work.
No exception, no trace the persist methode of entity manager is called but nothing in the database.
It's like if the transaction manager was not working.
this is my persistence configuration :
#Configuration
#EnableTransactionManagement
public class PersistenceConfiguration {
#Bean
public BasicDataSource driverManagerDataSource() {
final BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/xxx");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setValidationQuery("SELECT 1");
dataSource.setDefaultAutoCommit(false);
dataSource.setInitialSize(10);
dataSource.setMaxActive(20);
dataSource.setMaxIdle(10);
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean() {
final LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
localContainerEntityManagerFactoryBean.setDataSource(driverManagerDataSource());
localContainerEntityManagerFactoryBean.setPersistenceUnitName("xxxPersistenceUnitName");
localContainerEntityManagerFactoryBean.setPackagesToScan("org.xxx.model");
localContainerEntityManagerFactoryBean.setJpaVendorAdapter(new org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter());
final HashMap<String, String> map = new HashMap<>();
map.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
map.put("hibernate.hbm2ddl.auto", "update");
map.put("hibernate.show_sql", "false");
map.put("hibernate.format_sql", "false");
localContainerEntityManagerFactoryBean.setJpaPropertyMap(map);
localContainerEntityManagerFactoryBean.setJpaDialect(new org.springframework.orm.jpa.vendor.HibernateJpaDialect());
return localContainerEntityManagerFactoryBean;
}
#Bean
public JpaTransactionManager transactionManager() {
final JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(localContainerEntityManagerFactoryBean().getNativeEntityManagerFactory());
return jpaTransactionManager;
}
Dependence injection :
#Configuration
#Import({PersistenceConfiguration.class, UserConfiguration.class, SecurityConfiguration.class})
#ComponentScan(basePackages = "org.xxx")
#EnableWebMvc
public class XxxProjectConfiguration {
private static Logger LOG = Logger.getLogger(XxxProjectConfiguration.class);
#Autowired
private Environment env;
#PostConstruct
public void initApp() {
LOG.debug("Looking for Spring profiles...");
if (env.getActiveProfiles().length == 0) {
LOG.info("No Spring profile configured, running with default configuration.");
} else {
for (String profile : env.getActiveProfiles()) {
LOG.info("Detected Spring profile: {}" + profile);
}
}
}
#Autowired
private UserConfiguration userConfiguration;
// DAO
#Bean
public RelationshipDAO relationshipDAO() {
return new RelationshipDAOImpl();
}
#Bean
public RelationshipStatusDAO relationshipStatusDAO() {
return new RelationshipStatusDAOImpl();
}
#Bean
public MessageDAO messageDAO() {
return new MessageDAOImpl();
}
// Services
#Bean
public UserServiceImpl userService() {
return new UserServiceImpl(userConfiguration.userDAO(), relationshipDAO(), relationshipStatusDAO(), messageDAO());
}
}
And
#Configuration
#Import(PersistenceConfiguration.class)
public class UserConfiguration {
#Bean
public UserDAO userDAO() {
return new UserDAOImpl();
}
}
The service :
#Transactional(propagation=Propagation.SUPPORTS)
public class UserServiceImpl implements UserService, Serializable {
private static final long serialVersionUID = 1L;
private UserDAO userDAO;
private RelationshipDAO relationshipDAO;
private RelationshipStatusDAO relationshipStatusDAO;
private MessageDAO messageDAO;
public UserServiceImpl(final UserDAO userDAO, final RelationshipDAO relationshipDAO, final RelationshipStatusDAO relationshipStatusDAO, final MessageDAO messageDAO) {
this.userDAO = userDAO;
this.relationshipDAO = relationshipDAO;
this.relationshipStatusDAO = relationshipStatusDAO;
this.messageDAO = messageDAO;
}
#Override
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = UserServiceException.class)
public RelationshipStatus wantsRelationship(final long fromUserId, final long toUserId) throws UserServiceException {
try {
final Relationship relationship = new Relationship(new Date());
User fromUser = userDAO.get(fromUserId);
User toUser = new User(toUserId);
relationship.getUsers().add(fromUser);
fromUser.getRelationships().add(relationship);
relationship.getUsers().add(toUser);
toUser.getRelationships().add(relationship);
relationship.setWantsFromUserId(fromUserId);
final Message message = new Message(fromUserId, "Hi ! My name is " + fromUser.getFirstName() + ", I want to meet you");
relationship.getMessages().add(message);
relationship.setStatus(new RelationshipStatus(Status.WANTS));
relationshipDAO.persist(relationship);
return relationship.getStatus();
} catch (Exception e) {
throw new UserServiceException(e);
}
}
...
}
I do not understand anything...
The missing code is :
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
I am using Spring data Cassandra, to connect with Cassandra database, with configuration file extending AbstractCassandraConfiguration and overriding functions -
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
#Override
public String[] getEntityBasePackages() {
return new String[] {"com.example"};
}
My aim is to create tables automatically in Cassandra from the mentioned entities in com.example package with #Table annotation.
For example -
package com.example;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
#Table(value="goal")
public class Goal {
#PrimaryKey
private int id;
private String description;
public Goal(int id, String description) {
this.id = id;
this.description = description;
}
public Goal() {
}
public int getId() {
return id;
}
public String getDescription() {
return description;
}
public void setId(int id) {
this.id = id;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return "Goals [id=" + id + ", description=" + description + "]";
}
}
For this entity, with the given configuration, one table should get created during spring initialization, But it fails to do so.
No exception though, It just doesn't create anything in Cassandra.
Any help would be appreciated. Thanks.
I also faced similar requirement and following works for me:-
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints("127.0.0.1");
cluster.setPort(9042);
return cluster;
}
public String[] getEntityBasePackages() {
return new String[] { "com.user.entity" };
}
protected String getKeyspaceName() {
return "user_db";
}
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
Once your context is up, you can check the table in cassandra.
It is assumed your keyspace is already created in cassandra.
Please inform if you also want to create keyspace on the fly.
I am using Cassandra as a datasource in my Spring boot application and would like to initialize the database before the application starts.
Up to now what I have done is, I have defined a class "CassandraConfiguration" extending "AbstractCassandraConfiguration" class as in the examples which you can see below and I have a repository extending "CassandraRepository". When I create the keyspace and the table myself, the application works fine.
However, I want to create the keyspace and tables automatically while application is starting. In order to do that, I supplied a schema.cql file under resources folder but I could not make that script work.
Does anyone have any idea what can I do to create the keyspace(s) and tables automatically?
Thanks.
Edit: I am using Cassandra 2.0.9, spring-boot 1.3.2.RELEASE and datastax cassandra driver 2.1.6 versions.
CassandraConfiguration.java
#Configuration
#PropertySource(value = { "classpath:cassandra.properties" })
#EnableCassandraRepositories(basePackages = { "bla.bla.bla.repository" })
public class CassandraConfiguration extends AbstractCassandraConfiguration {
#Autowired
private Environment environment;
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints( environment.getProperty( "cassandra.contactpoints" ) );
cluster.setPort( Integer.parseInt( environment.getProperty( "cassandra.port" ) ) );
return cluster;
}
#Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
return new BasicCassandraMappingContext();
}
#Bean
public CassandraConverter converter() throws ClassNotFoundException {
return new MappingCassandraConverter(cassandraMapping());
}
#Override
protected String getKeyspaceName() {
return environment.getProperty( "cassandra.keyspace" );
}
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(environment.getProperty("cassandra.keyspace"));
session.setConverter(converter());
session.setSchemaAction(SchemaAction.NONE);
return session;
}
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
}
If you are still having problems with this, in Spring Boot 2 and SD Cassandra 2.0.3 you can do this straightforward Java configuration and setup everything out of the box.
#Configuration
#EnableCassandraRepositories(basePackages = "com.example.repository")
public class DbConfigAutoStart extends AbstractCassandraConfiguration {
/*
* Provide a contact point to the configuration.
*/
#Override
public String getContactPoints() {
return "exampleContactPointsUrl";
}
/*
* Provide a keyspace name to the configuration.
*/
#Override
public String getKeyspaceName() {
return "exampleKeyspace";
}
/*
* Automatically creates a Keyspace if it doesn't exist
*/
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
CreateKeyspaceSpecification specification = CreateKeyspaceSpecification
.createKeyspace("exampleKeyspace").ifNotExists()
.with(KeyspaceOption.DURABLE_WRITES, true).withSimpleReplication();
return Arrays.asList(specification);
}
/*
* Automatically configure a table if doesn't exist
*/
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
/*
* Get the entity package (where the entity class has the #Table annotation)
*/
#Override
public String[] getEntityBasePackages() {
return new String[] { "com.example.entity" };
}
And you are good to go
Your return type BasicCassandraMappingContext() might be deprecated. Use
#Bean
public CassandraMappingContext mappingContext() throws ClassNotFoundException {
CassandraMappingContext mappingContext= new CassandraMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
return mappingContext;
}
#Override
public String[] getEntityBasePackages() {
return new String[]{"base-package name of all your entity, annotated
with #Table"};
}
#Override
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
return CassandraEntityClassScanner.scan(getEntityBasePackages());
}
Instead of,
#Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
return new BasicCassandraMappingContext();
}
also set:
session.setSchemaAction(SchemaAction.RECREATE_DROP_UNUSED);
and Exclude:
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
get reference here.
I'm working with spring-boot 1.5.10.RELEASE and cassandra 3.0.16 but you can try downscaling the versions. To create the keyspace you can import the keyspacename from you application.yml or application.properties. Using the #Table annotation your tables should be generated automatically provided you have set the entity base package.
#Value("${cassandra.keyspace}")
private String keySpace;
#Override
public String[] getEntityBasePackages() {
return new String[]{"com.example.your.entities"};
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Arrays.asList(
CreateKeyspaceSpecification.createKeyspace()
.name(keySpace)
.ifNotExists()
);
}
Finally i got it working by adding setKeyspaceCreations(getKeyspaceCreations()) to the CassandraClusterFactoryBean Override and also make sure to enable #ComponentScan.
import com.datastax.driver.core.PlainTextAuthProvider;
import com.datastax.driver.core.policies.ConstantReconnectionPolicy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.*;
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.DropKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption;
import org.springframework.data.cassandra.repository.config.EnableReactiveCassandraRepositories;
import java.util.Arrays;
import java.util.List;
#Configuration
#EnableReactiveCassandraRepositories(basePackages = "com.company.domain.data")
public class CassandraConfig extends AbstractReactiveCassandraConfiguration{
#Value("${spring.data.cassandra.contactpoints}") private String contactPoints;
#Value("${spring.data.cassandra.port}") private int port;
#Value("${spring.data.cassandra.keyspace-name}") private String keyspace;
#Value("${spring.data.cassandra.username}") private String userName;
#Value("${spring.data.cassandra.password}") private String password;
#Value("${cassandra.basepackages}") private String basePackages;
#Override protected String getKeyspaceName() {
return keyspace;
}
#Override protected String getContactPoints() {
return contactPoints;
}
#Override protected int getPort() {
return port;
}
#Override public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
#Override
public String[] getEntityBasePackages() {
return new String[]{"com.company.domain.data"};
}
#Override
public CassandraClusterFactoryBean cluster() {
PlainTextAuthProvider authProvider = new PlainTextAuthProvider(userName, password);
CassandraClusterFactoryBean cluster=new CassandraClusterFactoryBean();
cluster.setJmxReportingEnabled(false);
cluster.setContactPoints(contactPoints);
cluster.setPort(port);
cluster.setAuthProvider(authProvider);
cluster.setKeyspaceCreations(getKeyspaceCreations());
cluster.setReconnectionPolicy(new ConstantReconnectionPolicy(1000));
return cluster;
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(keyspace)
.ifNotExists()
.with(KeyspaceOption.DURABLE_WRITES, true);
return Arrays.asList(specification);
}
#Override
protected List<DropKeyspaceSpecification> getKeyspaceDrops() {
return Arrays.asList(DropKeyspaceSpecification.dropKeyspace(keyspace));
}
}
The previous answers are based on AbstractCassandraConfiguration from spring-data-cassandra. If you use spring-boot then it can auto-configure Cassandra for you and there's no need to extend AbstractCassandraConfiguration. However, even in this case you need to do some work to automatically create the keyspace. I've settled on an auto-configuration added to our company's spring-boot starter, but you can also define it as a regular configuration in your application.
/**
* create the configured keyspace before the first cqlSession is instantiated. This is guaranteed by running this
* autoconfiguration before the spring-boot one.
*/
#ConditionalOnClass(CqlSession.class)
#ConditionalOnProperty(name = "spring.data.cassandra.create-keyspace", havingValue = "true")
#AutoConfigureBefore(CassandraAutoConfiguration.class)
public class CassandraCreateKeyspaceAutoConfiguration {
private static final Logger logger = LoggerFactory.getLogger(CassandraCreateKeyspaceAutoConfiguration.class);
public CassandraCreateKeyspaceAutoConfiguration(CqlSessionBuilder cqlSessionBuilder, CassandraProperties properties) {
// It's OK to mutate cqlSessionBuilder because it has prototype scope.
try (CqlSession session = cqlSessionBuilder.withKeyspace((CqlIdentifier) null).build()) {
logger.info("Creating keyspace {} ...", properties.getKeyspaceName());
session.execute(CreateKeyspaceCqlGenerator.toCql(
CreateKeyspaceSpecification.createKeyspace(properties.getKeyspaceName()).ifNotExists()));
}
}
}
In my case I've also added a configuration property to control the creation, spring.data.cassandra.create-keyspace, you may leave it out if you don't need the flexibility.
Note that spring-boot auto-configuration depends on certain configuration properties, here's what I have in my dev environment:
spring:
data:
cassandra:
keyspace-name: mykeyspace
contact-points: 127.0.0.1
port: 9042
local-datacenter: datacenter1
schema-action: CREATE_IF_NOT_EXISTS
create-keyspace: true
More details: spring-boot and Cassandra
My Spring Data Cassandra configuration looks like this:
#Configuration
#EnableCassandraRepositories(basePackages = {
"mypackage.repository.cassandra",
})
public class DistributedRepositoryConfiguration {
// ...
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(configuration.get().getKeyspace());
session.setConverter(converter());
session.setSchemaAction(SchemaAction.CREATE);
return session;
}
}
Generally, Spring Data Cassandra works in my project. However, when I start my application I have no tables created. Anyone who could tell me what I'm doing wrong?
It is not written well in documentation if you want to have automatic table creation you should tell cassandra where to look for entity classes:
<cassandra:mapping entity-base-packages="your.package" />
If you want to do the same using annotiation configuration you have to explicitly tell CassandraTemplate where to look for it. So
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(keyspaceName);
session.setConverter(converter());
session.setSchemaAction(SchemaAction.CREATE);
return session;
}
#Bean
public CassandraConverter converter() throws Exception {
return new MappingCassandraConverter(mappingContext());
}
#Bean
public CassandraMappingContext mappingContext() throws Exception {
BasicCassandraMappingContext bean = new BasicCassandraMappingContext();
bean.setInitialEntitySet(CassandraEntityClassScanner.scan(("package.with.your.entities")));
return bean;
}
To do it with ease I suggest using AbstractCassandraConfiguration and override methods which You need.
I checked the class AbstractCassandraConfiguration and found the following code:
public String[] getEntityBasePackages() {
return new String[] { getClass().getPackage().getName() };
}
Since my config class isn't in the main package, the component scan does not find my classes with the #Table annotation. So I override the method "getEntityBasePackages()" using my StartUp class and everything worked fine.
This is my config class:
#Configuration
public class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${spring.data.cassandra.keyspace-name}")
private String keyspaceName;
#Override
protected String getKeyspaceName() {
return keyspaceName;
}
#Override
public String[] getEntityBasePackages() {
return new String[]{AppStartup.class.getPackage().getName()};
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Collections.singletonList(CreateKeyspaceSpecification
.createKeyspace(keyspaceName)
.ifNotExists(true)
.with(KeyspaceOption.DURABLE_WRITES, true)
.withSimpleReplication());
}
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
}
Using this class, your application should create the required keyspace and tables to run.
If you using CassandraDataConfiguration just annotated you base class application with:
#EntityScan("mypackage.repository.cassandra")
The base package information will be used in CassandraDataAutoConfiguration.cassandraMapping method to add this package to cassandra mapping.