Connecting to Cassandra with Spring

使用 Spring 与 Apache Cassandra 时,第一个任务之一是通过 Spring IoC 容器创建 com.datastax.oss.driver.api.core.CqlSession 对象。您可以使用基于 Java 的 bean 元数据或基于 XML 的 bean 元数据来执行此操作。以下部分将对这些内容进行讨论。

对于那些不熟悉如何使用基于 Java 的 bean 元数据(而不是基于 XML 的元数据)配置 Spring 容器的人,请参阅参考文档 here 中的高级介绍以及详细文档https://www.iokays.com/spring-frameworkcore.html#beans-java-instantiating-container[此处]。

Registering a Session Instance by using Java-based Metadata

以下示例演示如何使用基于 Java 的 bean 元数据来注册 com.datastax.oss.driver.api.core.CqlSession 实例:

Example 1. Registering a com.datastax.oss.driver.api.core.CqlSession object by using Java-based bean metadata
/*
 * Copyright 2020-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.cassandra.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.datastax.oss.driver.api.core.CqlSession;

// tag::class[]
@Configuration
public class AppConfig {

	/*
	 * Use the standard Cassandra driver API to create a com.datastax.oss.driver.api.core.CqlSession instance.
	 */
	public @Bean CqlSession session() {
		return CqlSession.builder().withKeyspace("mykeyspace").build();
	}
}
// end::class[]

此方法让您可以使用您可能已知的标准 com.datastax.oss.driver.api.core.CqlSession API。

另一种方法是使用 Spring 的 CqlSessionFactoryBeancom.datastax.oss.driver.api.core.CqlSession 实例注册到容器。与直接实例化 com.datastax.oss.driver.api.core.CqlSession 实例相比,FactoryBean 方法还有额外的优势,即还向容器提供一个 ExceptionTranslator 实现,该实现将 Cassandra 异常转换为 Spring 可移植 DataAccessException 层次结构中的异常。https://www.iokays.com/spring-frameworkdata-access.html[Spring 的 DAO 支持功能] 中描述了此层次结构和 @Repository 的使用。

以下示例显示了基于 Java 的工厂类用法:

Example 2. Registering a com.datastax.oss.driver.api.core.CqlSession object by using Spring’s CqlSessionFactoryBean:
/*
 * Copyright 2020-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.cassandra.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.CqlSessionFactoryBean;

// tag::class[]
@Configuration
public class FactoryBeanAppConfig {

	/*
	 * Factory bean that creates the com.datastax.oss.driver.api.core.CqlSession instance
	 */
	@Bean
	public CqlSessionFactoryBean session() {

		CqlSessionFactoryBean session = new CqlSessionFactoryBean();
		session.setContactPoints("localhost");
		session.setKeyspaceName("mykeyspace");

		return session;
	}
}
// end::class[]

对对象映射和仓库支持使用 CassandraTemplate 需要一个 CassandraTemplateCassandraMappingContextCassandraConverter 以及启用的仓库支持。

以下示例显示了向对象映射注册组件以及启用仓库支持的方法:

Example 3. Registering components to configure object mapping and repository support
/*
 * Copyright 2020-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.cassandra.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.SessionFactory;
import org.springframework.data.cassandra.config.CqlSessionFactoryBean;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.config.SessionFactoryFactoryBean;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;

import com.datastax.oss.driver.api.core.CqlSession;

// tag::class[]
@Configuration
@EnableCassandraRepositories(basePackages = { "org.springframework.data.cassandra.example" })
public class CassandraConfig {

	@Bean
	public CqlSessionFactoryBean session() {

		CqlSessionFactoryBean session = new CqlSessionFactoryBean();
		session.setContactPoints("localhost");
		session.setKeyspaceName("mykeyspace");

		return session;
	}

	@Bean
	public SessionFactoryFactoryBean sessionFactory(CqlSession session, CassandraConverter converter) {

		SessionFactoryFactoryBean sessionFactory = new SessionFactoryFactoryBean();
		sessionFactory.setSession(session);
		sessionFactory.setConverter(converter);
		sessionFactory.setSchemaAction(SchemaAction.NONE);

		return sessionFactory;
	}

	@Bean
	public CassandraMappingContext mappingContext() {
		return new CassandraMappingContext();
	}

	@Bean
	public CassandraConverter converter(CqlSession cqlSession, CassandraMappingContext mappingContext) {

		MappingCassandraConverter cassandraConverter = new MappingCassandraConverter(mappingContext);
		cassandraConverter.setUserTypeResolver(new SimpleUserTypeResolver(cqlSession));

		return cassandraConverter;
	}

	@Bean
	public CassandraOperations cassandraTemplate(SessionFactory sessionFactory, CassandraConverter converter) {
		return new CassandraTemplate(sessionFactory, converter);
	}
}
// end::class[]

创建注册 Spring Data for Apache Cassandra 组件的配置类可能是一项艰巨的挑战,因此 Spring Data for Apache Cassandra 提供了一个预建的配置支持类。从 AbstractCassandraConfiguration 扩展的类注册 Spring Data for Apache Cassandra 用 Bean。AbstractCassandraConfiguration 让你能提供各种配置选项,例如初始实体、默认查询选项、池选项、套接字选项等等。AbstractCassandraConfiguration 还支持你根据初始实体(如果有此类实体)生成模式。从 AbstractCassandraConfiguration 扩展要求你至少通过实现 getKeyspaceName 方法提供键空间名称。以下示例显示了如何使用 AbstractCassandraConfiguration 注册 Bean:

Example 4. Registering Spring Data for Apache Cassandra beans by using AbstractCassandraConfiguration
/*
 * Copyright 2020-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.cassandra.example;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;

// tag::class[]
@Configuration
public class CassandraConfiguration extends AbstractCassandraConfiguration {

	/*
	 * Provide a contact point to the configuration.
	 */
	@Override
	public String getContactPoints() {
		return "localhost";
	}

	/*
	 * Provide a keyspace name to the configuration.
	 */
	@Override
	public String getKeyspaceName() {
		return "mykeyspace";
	}
}
// end::class[]

Abstract…Configuration 类为从应用程序中使用 Cassandra 连接所有必需的 Bean。此配置假设一个 CqlSession 并通过 SessionFactory 将其连接到诸如 CqlTemplate 的相关组件。如果你想自定义 CqlSession 的创建,那么你可以提供一个 SessionBuilderConfigurer 函数来自定义 CqlSessionBuilder。这很有用,例如为 Astra 提供一个 Cloud 连接包。

Example 5. Connecting to Astra through AbstractCassandraConfiguration
/*
 * Copyright 2020-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.cassandra.example;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;

// tag::class[]
@Configuration
public class CustomizedCassandraConfiguration extends AbstractCassandraConfiguration {

	/*
	 * Customize the CqlSession through CqlSessionBuilder.
	 */
	@Override
	protected SessionBuilderConfigurer getSessionBuilderConfigurer() {

		Path connectBundlePath = …;

		return builder -> builder
				.withCloudSecureConnectBundle(Path.of(connectBundlePath));
	}

	/*
	 * Provide a keyspace name to the configuration.
	 */
	@Override
	public String getKeyspaceName() {
		return "mykeyspace";
	}

}
// end::class[]

XML Configuration

此部分介绍如何使用 XML 配置 Spring Data Cassandra。

虽然我们仍支持命名空间配置,但我们通常建议使用 Java-based Configuration

Externalizing Connection Properties

要 externalize 连接属性,你应该先创建一个属性文件,其中包含连接到 Cassandra 所需的信息。contactpointskeyspace 是必需字段。

以下示例显示了名为 cassandra.properties 的属性文件:

cassandra.contactpoints=10.1.55.80:9042,10.1.55.81:9042
cassandra.keyspace=showcase

在接下来的两个示例中,我们使用 Spring 将这些属性加载到 Spring 环境。

Registering a Session Instance by using XML-based Metadata

虽然你可以使用 Spring 的传统 <beans/> XML 命名空间来向容器注册 com.datastax.oss.driver.api.core.CqlSession 的实例,但 XML 可能非常冗长,因为它很通用。XML 命名空间是配置常用对象(例如 CqlSession 实例)的更佳选择。cassandra 命名空间让你可以创建一个 CqlSession 实例。

以下示例显示了如何配置 cassandra 命名空间:

Example 6. XML schema to configure Cassandra by using the cassandra namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
  xsi:schemaLocation="
    http://www.springframework.org/schema/data/cassandra
    https://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- Default bean name is 'cassandraSession' -->
  <cassandra:session contact-points="localhost" port="9042">
    <cassandra:keyspace action="CREATE_DROP" name="mykeyspace" />
  </cassandra:session>

  <cassandra:session-factory>
    <cassandra:script
            location="classpath:/org/springframework/data/cassandra/config/schema.cql"/>
  </cassandra:session-factory>
</beans>

更高级的 Cassandra 配置的 XML 配置元素如下所示。这些元素都使用默认 Bean 名称来保持配置代码的简洁和可读性。

尽管前面的示例展示了使用 Spring 配置连接 Cassandra 是多么容易,但还有许多其他选项。基本上,DataStax Java Driver 中的任何可用的选项在 Spring Data for Apache Cassandra 配置中也可用。这包括但不限于认证、负载平衡策略、重试策略和池选项。所有 Spring Data for Apache Cassandra 方法名称和 XML 元素的命名与 Driver 上的配置选项完全相同(或尽可能接近),以便对任何现有的 Driver 配置进行映射应该是直接的。以下示例展示了如何使用 XML 配置 Spring Data 组件

Example 7. Configuring Spring Data components by using XML
<!-- Loads the properties into the Spring Context and uses them to fill
in placeholders in the bean definitions -->
<context:property-placeholder location="classpath:cassandra.properties" />

<!-- REQUIRED: The Cassandra Session -->
<cassandra:session contact-points="${cassandra.contactpoints}" keyspace-name="${cassandra.keyspace}" />

<!-- REQUIRED: The default Cassandra mapping context used by `CassandraConverter` -->
<cassandra:mapping>
  <cassandra:user-type-resolver keyspace-name="${cassandra.keyspace}" />
</cassandra:mapping>

<!-- REQUIRED: The default Cassandra converter used by `CassandraTemplate` -->
<cassandra:converter />

<!-- REQUIRED: The Cassandra template is the foundation of all Spring
Data Cassandra -->
<cassandra:template id="cassandraTemplate" />

<!-- OPTIONAL: If you use Spring Data for Apache Cassandra repositories, add
your base packages to scan here -->
<cassandra:repositories base-package="org.spring.cassandra.example.repo" />