初始化 DataSource

org.springframework.jdbc.datasource.init 包提供了初始化现有 DataSource 的支持。嵌入式数据库支持为应用程序创建和初始化 DataSource 提供了一个选项。但是,有时您可能需要初始化在某个服务器上运行的实例。

使用 Spring XML 初始化数据库

如果您想初始化数据库并能提供对 DataSource bean 的引用,您可以在 spring-jdbc 命名空间中使用 initialize-database 标签。

<jdbc:initialize-database data-source="dataSource">
	<jdbc:script location="classpath:com/foo/sql/db-schema.sql"/>
	<jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/>
</jdbc:initialize-database>

前面的示例针对数据库运行了两个指定的脚本。第一个脚本创建模式,第二个脚本用测试数据集填充表。脚本位置也可以是带有通配符的模式,采用 Spring 中资源常用的 Ant 样式(例如,classpath*:/com/foo/**/sql/*-data.sql)。如果您使用模式,脚本将按照其 URL 或文件名的字典顺序运行。

数据库初始化器的默认行为是无条件地运行提供的脚本。这可能不总是您想要的,例如,如果您针对已包含测试数据的数据库运行脚本。通过遵循常见的模式(如前面所示,先创建表然后插入数据)可以减少意外删除数据的可能性。如果表已存在,第一步将失败。

但是,为了更好地控制现有数据的创建和删除,XML 命名空间提供了一些额外的选项。第一个是切换初始化开启和关闭的标志。您可以根据环境设置此标志(例如,从系统属性或环境 bean 中获取布尔值)。以下示例从系统属性中获取值:

<jdbc:initialize-database data-source="dataSource"
	enabled="#{systemProperties.INITIALIZE_DATABASE}"> (1)
	<jdbc:script location="..."/>
</jdbc:initialize-database>
1 从名为 INITIALIZE_DATABASE 的系统属性中获取 enabled 的值。

控制现有数据处理方式的第二个选项是更容忍失败。为此,您可以控制初始化器忽略它从脚本运行的 SQL 中的某些错误的能力,如下例所示:

<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
	<jdbc:script location="..."/>
</jdbc:initialize-database>

在前面的示例中,我们表示我们期望有时脚本会针对空数据库运行,并且脚本中存在一些 DROP 语句,因此这些语句会失败。所以失败的 SQL DROP 语句将被忽略,但其他失败将导致异常。如果您的 SQL 方言不支持 DROP …​ IF EXISTS(或类似)但您想在重新创建所有测试数据之前无条件地删除它们,这将非常有用。在这种情况下,第一个脚本通常是一组 DROP 语句,后跟一组 CREATE 语句。

ignore-failures 选项可以设置为 NONE(默认值)、DROPS(忽略失败的删除)或 ALL(忽略所有失败)。

每个语句应由 ; 或换行符分隔,如果脚本中根本没有 ; 字符。您可以全局控制或逐个脚本控制,如下例所示:

<jdbc:initialize-database data-source="dataSource" separator="@@"> (1)
	<jdbc:script location="classpath:com/myapp/sql/db-schema.sql" separator=";"/> (2)
	<jdbc:script location="classpath:com/myapp/sql/db-test-data-1.sql"/>
	<jdbc:script location="classpath:com/myapp/sql/db-test-data-2.sql"/>
</jdbc:initialize-database>
1 将分隔符脚本设置为 @@
2 db-schema.sql 的分隔符设置为 ;

在此示例中,两个 test-data 脚本使用 @@ 作为语句分隔符,而只有 db-schema.sql 使用 ;。此配置指定默认分隔符为 @@,并针对 db-schema 脚本覆盖此默认值。

如果您需要比 XML 命名空间提供的更多控制,可以直接使用 DataSourceInitializer 并将其定义为应用程序中的组件。

依赖于数据库的其他组件的初始化

一大类应用程序(在 Spring 上下文启动后才使用数据库的应用程序)可以毫无问题地使用数据库初始化器。如果您的应用程序不属于此类,您可能需要阅读本节的其余部分。

数据库初始化器依赖于 DataSource 实例,并在其初始化回调中运行提供的脚本(类似于 XML bean 定义中的 init-method、组件中的 @PostConstruct 方法或实现 InitializingBean 的组件中的 afterPropertiesSet() 方法)。如果其他 bean 依赖于相同的数据库并在初始化回调中使用该数据库,则可能会出现问题,因为数据尚未初始化。一个常见的例子是缓存,它急切地初始化并在应用程序启动时从数据库加载数据。

为了解决这个问题,您有两个选择:将您的缓存初始化策略更改为稍后阶段,或确保数据库初始化器首先初始化。

如果应用程序在您的控制之下,则更改缓存初始化策略可能很容易,否则则不然。实现此目的的一些建议包括:

  • 让缓存在首次使用时延迟初始化,这可以提高应用程序启动时间。

  • 让您的缓存或初始化缓存的单独组件实现 LifecycleSmartLifecycle。当应用程序上下文启动时,您可以通过设置 SmartLifecycleautoStartup 标志来自动启动它,并且您可以通过调用封闭上下文的 ConfigurableApplicationContext.start() 来手动启动 Lifecycle

  • 使用 Spring ApplicationEvent 或类似的自定义观察者机制来触发缓存初始化。ContextRefreshedEvent 始终由上下文在准备好使用时发布(所有 bean 都已初始化之后),因此它通常是一个有用的钩子(这是 SmartLifecycle 默认的工作方式)。

确保数据库初始化器首先初始化也可能很容易。实现此目的的一些建议包括:

  • 依赖 Spring BeanFactory 的默认行为,即 bean 按照注册顺序初始化。您可以通过采用 XML 配置中一组 <import/> 元素的常见做法来轻松安排此操作,这些元素对应用程序模块进行排序,并确保数据库和数据库初始化首先列出。

  • DataSource 和使用它的业务组件分开,并通过将它们放在单独的 ApplicationContext 实例中来控制它们的启动顺序(例如,父上下文包含 DataSource,子上下文包含业务组件)。这种结构在 Spring Web 应用程序中很常见,但可以更普遍地应用。

© . This site is unofficial and not affiliated with VMware.