此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Cassandra 4.4.0! |
架构管理
Apache Cassandra 是一种数据存储,在任何数据交互之前都需要架构定义。 Spring Data for Apache Cassandra 可以支持您创建架构。
CQL 规格
CQL 规范是表示 CQL DDL作(例如表创建或键空间删除)的抽象。 CQL 对象类型包括以下规格:
-
键空间
-
桌子
-
指数
-
用户自定义类型
Cassandra 的变体可以支持物化视图、用户定义的函数、角色和更多对象类型。 Spring Data for Apache Cassandra 仅提供上述类型的规范。 |
这些可用于通过 Fluent 接口 CREATE、ALTER 和 DROP CQL 对象。SpecificationBuilder
是构建此类规范的入口点。
您可以稍后使用CqlGenerator.toCql(…)
轻松从规范呈现 CQL。
请参阅以下示例以创建密钥空间、表创建和索引创建的规范。
CqlSpecification createKeyspace = SpecificationBuilder.createKeyspace("my_keyspace")
.with(KeyspaceOption.REPLICATION, KeyspaceAttributes.newSimpleReplication())
.with(KeyspaceOption.DURABLE_WRITES, true);
// results in CREATE KEYSPACE my_keyspace WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} AND durable_writes = true
String cql = CqlGenerator.toCql(createKeyspace);
CreateTableSpecification createTable = CreateTableSpecification.createTable("my_table")
.partitionKeyColumn("last_name", DataTypes.TEXT)
.partitionKeyColumn("first_name", DataTypes.TEXT)
.column("age", DataTypes.INT);
// results in CREATE TABLE my_table (last_name text, first_name text, age int, PRIMARY KEY(last_name, first_name))
String cql = CqlGenerator.toCql(createTable);
CreateIndexSpecification spec = SpecificationBuilder.createIndex()
.tableName("mytable").keys().columnName("column");
// results in CREATE INDEX ON mytable (KEYS(column))
String cql = CqlGenerator.toCql(createTable);
您可以将规范与配置 API 一起使用来定义密钥空间创建和架构作。
Keyspaces 和生命周期脚本
首先要做的是 Cassandra 密钥空间。
键空间是共享相同复制因子和复制策略的表的逻辑分组。
Keyspace 管理位于CqlSession
配置,它具有KeyspaceSpecification
以及启动和关闭 CQL 脚本执行。
使用规范声明密钥空间允许创建和删除密钥空间。 它从规范中派生 CQL,因此您无需自己编写 CQL。 以下示例使用 XML 指定 Cassandra 密钥空间:
@Configuration
public class CreateKeyspaceConfiguration extends AbstractCassandraConfiguration implements BeanClassLoaderAware {
@Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
CreateKeyspaceSpecification specification = SpecificationBuilder.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"));
}
// ...
}
<?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">
<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>
</beans>
Keyspace 创建允许快速引导,而无需外部 Keyspace 管理。 这在某些情况下可能很有用,但应谨慎使用。 在应用程序关闭时删除密钥空间将从密钥空间的表中删除密钥空间和所有数据。 |
初始化SessionFactory
这org.springframework.data.cassandra.core.cql.session.init
package 支持初始化现有的SessionFactory
.
有时可能需要初始化在某个位置的服务器上运行的密钥空间。
初始化 Keyspace
您可以提供在CqlSession
initialization 和 shutdown,如下面的 Java 配置示例所示:
@Configuration
public class KeyspacePopulatorConfiguration extends AbstractCassandraConfiguration {
@Nullable
@Override
protected KeyspacePopulator keyspacePopulator() {
return new ResourceKeyspacePopulator(new ClassPathResource("com/foo/cql/db-schema.cql"),
new ClassPathResource("com/foo/cql/db-test-data.cql"));
}
@Nullable
@Override
protected KeyspacePopulator keyspaceCleaner() {
return new ResourceKeyspacePopulator(scriptOf("DROP TABLE my_table;"));
}
// ...
}
<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 或 filename 的词法顺序运行。
keyspace initializer 的默认行为是无条件地运行提供的脚本。 这可能并不总是您想要的 — 例如,如果您针对已经包含测试数据的 keyspace 运行脚本。 通过遵循先创建表,然后插入数据的常见模式(如前所示),可以降低意外删除数据的可能性。 如果表已存在,则第一步将失败。
但是,为了更好地控制现有数据的创建和删除,XML 名称空间提供了一些其他选项。 第一个是用于打开和关闭初始化的标志。 您可以根据环境进行设置(例如,从系统属性或环境 Bean 中提取布尔值)。 以下示例从 system 属性获取值:
<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory"
enabled="#{systemProperties.INITIALIZE_KEYSPACE}"> (1)
<cassandra:script location="..."/>
</cassandra:initialize-database>
1 | 获取 的值enabled 从名为INITIALIZE_KEYSPACE . |
控制现有数据发生情况的第二个选项是对故障的容忍度更高。 为此,您可以控制初始化器忽略它从脚本执行的 CQL 中的某些错误的能力,如下例所示:
@Configuration
public class KeyspacePopulatorFailureConfiguration extends AbstractCassandraConfiguration {
@Nullable
@Override
protected KeyspacePopulator keyspacePopulator() {
ResourceKeyspacePopulator populator = new ResourceKeyspacePopulator(
new ClassPathResource("com/foo/cql/db-schema.cql"));
populator.setIgnoreFailedDrops(true);
return populator;
}
// ...
}
<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory" ignore-failures="DROPS">
<cassandra:script location="..."/>
</cassandra:initialize-database>
在前面的示例中,我们表示我们预期有时脚本是针对空 keyspace 运行的,并且有一些DROP
因此会失败的语句。
所以 CQL 失败了DROP
语句将被忽略,但其他失败将导致异常。
如果您不想使用 tu use 支持,这很有用DROP … IF EXISTS
(或类似方法),但您希望在重新创建之前无条件地删除所有测试数据。
在这种情况下,第一个脚本通常是一组DROP
语句,后跟一组CREATE
语句。
这ignore-failures
选项可以设置为NONE
(默认值)、DROPS
(忽略失败的放置),或ALL
(忽略所有失败)。
如果脚本中根本不存在字符,则每个语句都应该用 或 new line 分隔。
您可以全局控制该作,也可以逐个脚本控制,如下例所示:;
;
@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;
}
// ...
}
<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>
在此示例中,两个test-data
脚本用作语句分隔符,并且只有@@
db-schema.cql
使用。
此配置指定默认分隔符为 并覆盖;
@@
db-schema
脚本。
如果您需要的控制比从 XML 命名空间获得更多的控制权,则可以使用SessionFactoryInitializer
直接将其定义为应用程序中的一个组件。
依赖于 Keyspace 的其他组件的初始化
一大类应用程序(那些在 Spring 上下文启动之前不使用数据库的应用程序)可以使用数据库初始化器,而不会进一步复杂化。 如果您的应用程序不是其中之一,则可能需要阅读本节的其余部分。
数据库初始化器依赖于SessionFactory
实例并运行其初始化回调中提供的脚本(类似于init-method
在 XML Bean 定义中,@PostConstruct
方法,或者afterPropertiesSet()
方法,该组件实现了InitializingBean
).
如果其他 bean 依赖于相同的数据源并在初始化回调中使用会话工厂,则可能存在问题,因为数据尚未初始化。
一个常见的示例是缓存,它在应用程序启动时预先初始化并从数据库加载数据。
要解决此问题,您有两种选择:将缓存初始化策略更改为稍后阶段,或者确保先初始化密钥空间初始化器。
如果应用程序由您控制,则更改缓存初始化策略可能很容易。 有关如何实现此功能的一些建议包括:
-
使缓存在首次使用时延迟初始化,从而缩短应用程序启动时间。
-
让您的缓存或初始化缓存的单独组件实现
Lifecycle
或SmartLifecycle
. 当应用程序上下文启动时,您可以自动启动SmartLifecycle
通过设置其autoStartup
标志,您可以手动启动Lifecycle
通过调用ConfigurableApplicationContext.start()
在封闭的上下文中。 -
使用 Spring
ApplicationEvent
或类似的自定义观察者机制来触发缓存初始化。ContextRefreshedEvent
在可供使用时(在所有 bean 都已初始化之后)始终由上下文发布,因此这通常是一个有用的钩子(这就是SmartLifecycle
默认有效)。
确保首先初始化 keyspace initializer 也很容易。 有关如何实现此功能的一些建议包括:
-
依赖 Spring 的默认行为
BeanFactory
,即 bean 按注册顺序初始化。 您可以通过采用一组<import/>
元素,用于对应用程序模块进行排序,并确保首先列出数据库和数据库初始化。 -
将
SessionFactory
以及使用它并通过将它们放在单独的ApplicationContext
实例(例如,父上下文包含SessionFactory
,子上下文包含业务组件)。 这种结构在 Spring Web 应用程序中很常见,但可以更普遍地应用。 -
使用 Schema management for Tables 和 User-defined Types 来使用 Spring Data Cassandra 的内置模式生成器初始化键空间。
表和用户定义的类型
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 还会删除应用程序未知的表和类型。 |
为 Schema Management 启用表和用户定义类型
基于元数据的映射 解释了使用约定和注释进行对象映射。
为了防止将不需要的类创建为表或类型,架构管理仅对带有@Table
和用@UserDefinedType
.
通过扫描 Classpath 来发现实体。
实体扫描需要一个或多个基础包。
使用TupleValue
请勿提供任何键入详细信息。
因此,您必须使用@CassandraType(type = TUPLE, typeArguments = …)
以指定所需的列类型。
以下示例演示如何在 XML 配置中指定实体基础包:
@Configuration
public class EntityBasePackagesConfiguration extends AbstractCassandraConfiguration {
@Override
public String[] getEntityBasePackages() {
return new String[] { "com.foo", "com.bar" };
}
// ...
}
<?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">
<cassandra:mapping entity-base-packages="com.foo,com.bar"/>
</beans>