|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
初始化一个DataSource
The org.springframework.jdbc.datasource.init package provides support for initializing
an existing DataSource. The embedded database support provides one option for creating
and initializing a DataSource for an application. However, you may sometimes need to initialize
an instance that runs on a server somewhere.
使用Spring XML初始化数据库
如果您想初始化一个数据库并且可以提供对DataSource bean的引用,可以在initialize-database命名空间中使用spring-jdbc标签:
<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中的资源(例如,
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 | 从名为 enabled 的系统属性中获取值 INITIALIZE_DATABASE。 |
控制现有数据如何处理的第二种方法是更宽容地处理失败。为此,你可以控制初始化程序在运行脚本中的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依赖于相同的数据源,并在初始化回调中使用数据源,可能会出现问题,因为数据尚未初始化。一个常见的例子是缓存,在应用启动时会立即初始化并从数据库加载数据。
为解决此问题,你有两个选项:将缓存初始化策略更改为较晚的阶段,或确保数据库初始化器首先被初始化。
更改你的缓存初始化策略,如果应用程序在你的控制之下,可能会很容易。 一些实现此功能的建议包括:
-
在首次使用时延迟初始化缓存,这可以提高应用程序的启动速度。
-
让你的缓存或单独的初始化缓存的组件实现
Lifecycle或SmartLifecycle。当应用上下文启动时,可以通过设置其autoStartup标志自动启动一个SmartLifecycle,也可以通过在封装上下文中调用ConfigurableApplicationContext.start()手动启动一个Lifecycle。 -
使用 Spring
ApplicationEvent或类似的自定义观察者机制来触发缓存初始化。ContextRefreshedEvent在上下文准备好使用时(在所有 bean 都已初始化之后)总是会被发布,因此这通常是一个有用的钩子(这是SmartLifecycle默认的工作方式)。
确保数据库初始化器首先被初始化也可以很容易。实现此目的的一些建议包括:
-
依赖 Spring
BeanFactory的默认行为,即按注册顺序初始化 beans。您可以通过在 XML 配置中采用一组按顺序排列应用程序模块的<import/>元素的常见做法,轻松实现这一点,并确保数据库和数据库初始化首先列出。 -
将
DataSource与使用它的业务组件分开,并通过将它们放在不同的ApplicationContext实例中来控制它们的启动顺序(例如,父上下文包含DataSource,子上下文包含业务组件)。这种结构在 Spring Web 应用程序中很常见,但可以更广泛地应用。