9.3.2.XML 配置
本节介绍如何使用 XML 配置 Spring Data Cassandra。
外部化连接属性
要外部化连接属性,您应该首先创建一个属性文件,其中包含连接到 Cassandra 所需的信息。contactpoints和keyspace是必填字段。
以下示例显示了我们的属性文件,名为cassandra.properties:
代码语言:javascript复制cassandra.contactpoints=10.1.55.80:9042,10.1.55.81:9042
cassandra.keyspace=showcase
在接下来的两个示例中,我们使用 Spring 将这些属性加载到 Spring 上下文中。
使用基于 XML 的元数据注册会话实例
虽然您可以使用 Spring 的传统<beans/>XML 命名空间向 com.datastax.oss.driver.api.core.CqlSession容器注册一个实例 ,但 XML 可能非常冗长,因为它是通用的。XML 名称空间是配置常用对象(例如CqlSession实例)的更好替代方法。该cassandra命名空间让你创建一个CqlSession实例。
以下示例显示了如何配置cassandra命名空间:
示例 58. 使用cassandra命名空间配置 Cassandra 的 XML 模式
代码语言:javascript复制<?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 驱动程序可用的任何选项也可在 Spring Data for Apache Cassandra 配置中使用。这包括但不限于身份验证、负载平衡策略、重试策略和池选项。所有 Spring Data for Apache Cassandra 方法名称和 XML 元素都与驱动程序上的配置选项完全(或尽可能接近)命名,以便映射任何现有的驱动程序配置应该是直接的。以下示例展示了如何使用 XML 配置 Spring Data 组件
示例 59. 使用 XML 配置 Spring Data 组件
代码语言:javascript复制<!-- 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" />
9.4.模式管理
Apache Cassandra 是一种数据存储,需要在任何数据交互之前进行模式定义。Spring Data for Apache Cassandra 可以支持您创建模式。
9.4.1.键空间和生命周期脚本
首先是一个 Cassandra 键空间。键空间是共享相同复制因子和复制策略的表的逻辑分组。Keyspace 管理位于CqlSession配置中,其中有KeyspaceSpecification启动和关闭 CQL 脚本执行。
使用规范声明键空间允许创建和删除键空间。它从规范中派生出 CQL,因此您无需自己编写 CQL。以下示例使用 XML 指定 Cassadra 键空间:
示例 60. 使用 XML 指定 Cassandra 键空间
代码语言:javascript复制<cassandra:session>
<cassandra:keyspace action="CREATE_DROP" durable-writes="true" name="my_keyspace">
<cassandra:replication class="NETWORK_TOPOLOGY_STRATEGY">
<cassandra:data-center name="foo" replication-factor="1" />
<cassandra:data-center name="bar" replication-factor="2" />
</cassandra:replication>
</cassandra:keyspace>
</cassandra:session>
您还可以使用 Java 配置指定 Cassandra 密钥空间,如以下示例所示:
示例 61. 使用 Java 配置指定 Cassandra 键空间
代码语言:javascript复制@Configuration
public class CreateKeyspaceConfiguration extends AbstractCassandraConfiguration implements BeanClassLoaderAware {
@Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace("my_keyspace")
.with(KeyspaceOption.DURABLE_WRITES, true)
.withNetworkReplication(DataCenterReplication.of("foo", 1), DataCenterReplication.of("bar", 2));
return Arrays.asList(specification);
}
@Override
protected List<DropKeyspaceSpecification> getKeyspaceDrops() {
return Arrays.asList(DropKeyspaceSpecification.dropKeyspace("my_keyspace"));
}
// ...
}
密钥空间创建允许快速引导,无需外部密钥空间管理。这对某些场景很有用,但应谨慎使用。在应用程序关闭时删除键空间会从键空间中的表中删除键空间和所有数据。
9.4.2.初始化一个SessionFactory
该 org.springframework.data.cassandra.core.cql.session.init包提供对初始化现有SessionFactory. 您有时可能需要初始化在某处服务器上运行的密钥空间。
初始化密钥空间
您可以CqlSession在配置的键空间中提供在初始化和关闭时执行的任意 CQL ,如以下 Java 配置示例所示:
代码语言:javascript复制@Configuration
public class KeyspacePopulatorConfiguration extends AbstractCassandraConfiguration {
@Nullable
@Override
protected KeyspacePopulator keyspacePopulator() {
return new ResourceKeyspacePopulator(scriptOf("CREATE TABLE my_table …"));
}
@Nullable
@Override
protected KeyspacePopulator keyspaceCleaner() {
return new ResourceKeyspacePopulator(scriptOf("DROP TABLE my_table;"));
}
// ...
}
如果要使用 XML 配置初始化数据库并且可以提供对SessionFactorybean的引用,则可以使用命名空间中的initialize-keyspace标记cassandra:
代码语言:javascript复制<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory">
<cassandra:script location="classpath:com/foo/cql/db-schema.cql"/>
<cassandra:script location="classpath:com/foo/cql/db-test-data.cql"/>
</cassandra:initialize-keyspace>
前面的示例针对键空间运行两个指定的脚本。第一个脚本创建模式,第二个脚本使用测试数据集填充表。脚本位置也可以是 Spring 中用于资源的通常 Ant 样式中的带通配符的模式(例如,classpath*:/com/foo/**/cql/*-data.cql)。如果使用模式,脚本将按其 URL 或文件名的词法顺序运行。
键空间初始值设定项的默认行为是无条件运行提供的脚本。这可能并不总是您想要的——例如,如果您针对已包含测试数据的键空间运行脚本。通过遵循先创建表然后插入数据的常见模式(如前面所示),可以降低意外删除数据的可能性。如果表已存在,则第一步将失败。
但是,为了更好地控制现有数据的创建和删除,XML 命名空间提供了一些附加选项。第一个是打开和关闭初始化的标志。您可以根据环境进行设置(例如从系统属性或环境 bean 中提取布尔值)。以下示例从系统属性中获取值:
代码语言:javascript复制<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory"
enabled="#{systemProperties.INITIALIZE_KEYSPACE}">
<cassandra:script location="..."/>
</cassandra:initialize-database>
enabled从名为 的系统属性获取值INITIALIZE_KEYSPACE。
控制现有数据发生的情况的第二个选项是更能容忍失败。为此,您可以控制初始化程序忽略它从脚本执行的 CQL 中的某些错误的能力,如以下示例所示:
代码语言:javascript复制<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory" ignore-failures="DROPS">
<cassandra:script location="..."/>
</cassandra:initialize-database>
在前面的示例中,我们说我们期望脚本有时针对空的键空间运行DROP,因此脚本中的某些语句会失败。所以失败的 CQLDROP语句将被忽略,但其他失败将导致异常。如果您不想使用支持DROP … IF EXISTS(或类似的),但您想在重新创建之前无条件地删除所有测试数据,这将很有用。在这种情况下,第一个脚本通常是一组DROP语句,然后是一组CREATE语句。
该ignore-failures选项可以设置为NONE(默认)、DROPS(忽略失败的丢弃)或ALL(忽略所有失败)。
;如果;脚本中根本不存在该字符,则每个语句都应由或 一个新行分隔。您可以通过脚本进行全局或脚本控制,如下例所示:
代码语言:javascript复制@Configuration
public class SessionFactoryInitializerConfiguration extends AbstractCassandraConfiguration {
@Bean
SessionFactoryInitializer sessionFactoryInitializer(SessionFactory sessionFactory) {
SessionFactoryInitializer initializer = new SessionFactoryInitializer();
initializer.setSessionFactory(sessionFactory);
ResourceKeyspacePopulator populator1 = new ResourceKeyspacePopulator();
populator1.setSeparator(";");
populator1.setScripts(new ClassPathResource("com/myapp/cql/db-schema.cql"));
ResourceKeyspacePopulator populator2 = new ResourceKeyspacePopulator();
populator2.setSeparator("@@");
populator2.setScripts(new ClassPathResource("classpath:com/myapp/cql/db-test-data-1.cql"), //
new ClassPathResource("classpath:com/myapp/cql/db-test-data-2.cql"));
initializer.setKeyspacePopulator(new CompositeKeyspacePopulator(populator1, populator2));
return initializer;
}
// ...
}
或者,您可以使用 XML 来配置SessionFactoryInitializer:
代码语言:javascript复制<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory" separator="@@">
<cassandra:script location="classpath:com/myapp/cql/db-schema.cql" separator=";"/>
<cassandra:script location="classpath:com/myapp/cql/db-test-data-1.cql"/>
<cassandra:script location="classpath:com/myapp/cql/db-test-data-2.cql"/>
</cassandra:initialize-keyspace>
将分隔符脚本设置为@@.
设置分隔符db-schema.cql来;。
在此示例中,两个test-data脚本@@用作语句分隔符,并且仅db-schema.cql使用;. 此配置指定默认分隔符是@@并覆盖db-schema脚本的默认值。
如果您需要比从 XML 命名空间获得更多的控制,您可以SessionFactoryInitializer直接使用并将其定义为应用程序中的组件。
依赖键空间的其他组件的初始化
一大类应用程序(那些在 Spring 上下文启动后才使用数据库的应用程序)可以使用数据库初始化程序而不会出现进一步的复杂情况。如果您的应用程序不是其中之一,您可能需要阅读本节的其余部分。
数据库初始化程序依赖于一个SessionFactory实例并运行其初始化回调中提供的脚本(类似于init-methodXML bean 定义中的 、@PostConstruct组件中的afterPropertiesSet()方法或实现 的组件中的方法InitializingBean)。如果其他 bean 依赖相同的数据源并在初始化回调中使用会话工厂,则可能存在问题,因为数据尚未初始化。一个常见的例子是缓存,它会在应用程序启动时急切地初始化并从数据库加载数据。
要解决此问题,您有两种选择:将缓存初始化策略更改为稍后阶段或确保首先初始化键空间初始化程序。
如果应用程序在您的控制之下而不是在其他情况下,则更改缓存初始化策略可能很容易。关于如何实现这一点的一些建议包括:
- 使缓存在第一次使用时延迟初始化,从而缩短应用程序启动时间。
- 让您的缓存或初始化缓存的单独组件实现Lifecycle或SmartLifecycle。当应用程序上下文启动时,您可以SmartLifecycle通过设置其autoStartup标志来自动启动 a ,并且您可以Lifecycle通过调用ConfigurableApplicationContext.start()封闭的上下文来手动启动 a 。
- 使用 SpringApplicationEvent或类似的自定义观察器机制来触发缓存初始化。ContextRefreshedEvent当上下文准备好使用时(在所有 bean 已经初始化之后),它总是由上下文发布,所以这通常是一个有用的钩子(这是SmartLifecycle默认情况下的工作方式)。
确保首先初始化键空间初始化器也很容易。关于如何实现这一点的一些建议包括:
- 依赖 Spring 的默认行为,BeanFactory即 bean 按注册顺序初始化。您可以通过采用<import/>XML 配置中的一组元素来对应用程序模块进行排序的常见做法,并确保首先列出数据库和数据库初始化,从而轻松地进行安排。
- 将SessionFactory使用它的业务组件和使用它的业务组件分开,并通过将它们放在单独的ApplicationContext实例中来控制它们的启动顺序(例如,父上下文包含SessionFactory,子上下文包含业务组件)。这种结构在 Spring Web 应用程序中很常见,但可以更普遍地应用。
- 使用表和用户定义类型的模式管理使用 Spring Data Cassandra 的内置模式生成器初始化键空间。
9.4.3.表和用户定义的类型
Spring Data for Apache Cassandra 使用适合您的数据模型的映射实体类来处理数据访问。您可以使用这些实体类来创建 Cassandra 表规范和用户类型定义。
架构创建与CqlSession初始化相关联SchemaAction。支持以下操作:
- SchemaAction.NONE: 没有创建或删除表或类型。这是默认设置。
- SchemaAction.CREATE:从用@Table注释的实体和用注释的类型创建表、索引和用户定义的类型@UserDefinedType。如果您尝试创建类型,现有表或类型会导致错误。
- SchemaAction.CREATE_IF_NOT_EXISTS: 喜欢SchemaAction.CREATE但IF NOT EXISTS应用。现有的表或类型不会导致任何错误,但可能会保持陈旧。
- SchemaAction.RECREATE:删除并重新创建已知要使用的现有表和类型。未在应用程序中配置的表和类型不会被删除。
- SchemaAction.RECREATE_DROP_UNUSED:删除所有表和类型并仅重新创建已知的表和类型。
SchemaAction.RECREATE并 SchemaAction.RECREATE_DROP_UNUSED删除您的表并丢失所有数据。 RECREATE_DROP_UNUSED还会删除应用程序未知的表和类型。
为模式管理启用表和用户定义的类型
基于元数据的映射解释了具有约定和注释的对象映射。为了防止将不需要的类创建为表或类型,模式管理仅对用 注释的实体@Table和用注释的用户定义类型有效@UserDefinedType。通过扫描类路径发现实体。实体扫描需要一个或多个基础包。使用的元组类型列TupleValue不提供任何类型详细信息。因此,您必须注释此类列属性@CassandraType(type = TUPLE, typeArguments = …) 以指定所需的列类型。
以下示例显示了如何在 XML 配置中指定实体基础包:
示例 62. 使用 XML 配置指定实体基础包
代码语言:javascript复制<cassandra:mapping entity-base-packages="com.foo,com.bar"/>
以下示例显示了如何在 Java 配置中指定实体基础包:
示例 63.使用 Java 配置指定实体基础包
代码语言:javascript复制@Configuration
public class EntityBasePackagesConfiguration extends AbstractCassandraConfiguration {
@Override
public String[] getEntityBasePackages() {
return new String[] { "com.foo", "com.bar" };
}
// ...
}