跳到主要内容
版本:7.0.3

初始化一个 DataSource

Hunyuan 7b 中英对照 Initializing a DataSource Initializing a 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中用于资源文件的引用(例如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>
  • 从名为 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(忽略失败的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>
  • 将分隔符脚本设置为 @@

  • 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或类似的自定义观察器机制来触发缓存初始化。当上下文准备就绪(所有bean都已初始化)时,总会发布ContextRefreshedEvent事件,因此这通常是一个有用的时机点(默认情况下,SmartLifecycle就是通过这种方式工作的)。

确保数据库初始化器首先被初始化也可以很简单。关于如何实现这一点的一些建议包括:

  • 依赖于Spring BeanFactory的默认行为,即bean是按照注册顺序进行初始化的。你可以通过在XML配置中采用一组<import/>元素来轻松地安排这一顺序,这些元素会定义应用程序模块的顺序,并确保数据库及其初始化操作被列在首位。

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