# 3.10初始化数据源

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

## 3.10.1 使用Spring XML初始化数据库

如果要初始化数据库，并且可以提供对DataSource bean的引用，则可以在spring-jdbc命名空间中使用initialize-database标记：

```markup
<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中提取布尔值）。以下示例从系统属性获取值：

```markup
<jdbc:initialize-database data-source="dataSource"
    enabled="#{systemProperties.INITIALIZE_DATABASE}"> 
    <jdbc:script location="..."/>
</jdbc:initialize-database>
```

第二种选择是容忍失败。 为此，您可以控制初始化程序忽略脚本执行的SQL中某些错误的能力，如以下示例所示：

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

在前面的示例中，我们说我们期望有时脚本是针对空数据库运行的，并且脚本中有一些DROP语句可能因此失败。 因此失败的SQL DROP语句将被忽略，但是其他失败将导致异常。 如果您的SQL方言不支持DROP ......如果存在（或类似），但您想在重新创建之前无条件删除所有测试数据，则此功能很有用。 在那种情况下，第一个脚本通常是一组DROP语句，然后是一组CREATE语句。

可以将ignore-failures选项设置为NONE（默认值），DROPS（忽略失败的丢弃）或ALL（忽略所有失败）。

每个语句都应用; 或换行结束。 您可以全局控制该脚本，也可以按脚本控制，如以下示例所示：

```markup
<jdbc:initialize-database data-source="dataSource" separator="@@"> 
    <jdbc:script location="classpath:com/myapp/sql/db-schema.sql" separator=";"/> 
    <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使用;。此配置指定默认分隔符为@@，并覆盖db-schema脚本的默认分隔符。

如果您需要比从XML名称空间获得更多控制权，则可以直接使用DataSourceInitializer并将其定义为应用程序中的组件。

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

大量的应用程序（那些在Spring上下文启动之后才使用数据库的应用程序）可以使用数据库初始化程序，而不会带来更多麻烦。如果您的应用程序不是其中之一，则可能需要阅读本节的其余部分。

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

要解决此问题，您有两个选择：将高速缓存初始化策略更改为以后的阶段，或者确保首先初始化数据库初始化程序。

如果应用程序在您的控制之下，则更改缓存初始化策略可能很容易，否则就不那么容易。有关如何实现这一点的一些建议包括：

* 使缓存在首次使用时延迟初始化，从而缩短了应用程序的启动时间。
* 让您的缓存或单独的组件初始化缓存实现Lifecycle或SmartLifecycle。当应用程序上下文启动时，您可以通过设置其SmartStartup标志来自动启动SmartLifecycle，并且可以通过在封闭的上下文中调用ConfigurableApplicationContext.start（）来手动启动Lifecycle。
* 使用Spring ApplicationEvent或类似的自定义观察器机制来触发缓存初始化。 ContextRefreshedEvent总是在准备好使用时（在所有bean都初始化之后）由上下文发布，因此通常是一个有用的钩子（默认情况下，SmartLifecycle的工作方式）。

确保首先初始化数据库初始化程序也很容易。关于如何实现这一点的一些建议包括：

* 依靠Spring BeanFactory的默认行为，即按注册顺序初始化bean。通过采用XML配置中的一组\<import />元素（对应用程序模块进行排序）的通用做法，并确保首先列出数据库和数据库初始化。
* 通过将数据源和使用它的业务组件分开，并通过将它们放在单独的ApplicationContext实例中来控制它们的启动顺序（例如，父上下文包含DataSource，子上下文包含业务组件）。这种结构在Spring Web应用程序中很常见，但可以更普遍地应用。
