初始化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。当应用程序上下文启动时,您可以通过设置其autoStartup标志自动启动SmartLifecycle,并且可以通过在封闭上下文上调用ConfigurableApplicationContext.start()来手动启动Lifecycle

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

确保数据库初始化程序首先初始化也很容易。一些实现方法的建议包括:

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

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