Initializing a DataSource
`org.springframework.jdbc.datasource.init`包为初始化现有的`DataSource`提供了支持。嵌入式数据库支持为应用程序创建和初始化`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.
Initializing a Database by Using Spring XML
如果您想要初始化一个数据库并且可以提供对`DataSource` bean 的引用,则可以在`spring-jdbc`命名空间中使用`initialize-database`标签:
If you want to initialize a database and you can provide a reference to a DataSource
bean, you can use the initialize-database
tag in the spring-jdbc
namespace:
<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 或文件名的词法顺序来运行脚本。
The preceding example runs the two specified scripts against the database. The first
script creates a schema, and the second populates tables with a test data set. The script
locations can also be patterns with wildcards in the usual Ant style used for resources
in Spring (for example,
classpath*:/com/foo/*/sql/-data.sql
). If you use a
pattern, the scripts are run in the lexical order of their URL or filename.
数据库初始化器的默认行为是无条件运行所提供的脚本。这可能并不总是您想要的,例如,如果您对已经包含测试数据的数据库运行脚本。通过遵循先创建表然后插入数据的常见模式(如前所示),可以减少意外删除数据的可能性。如果表已经存在,则第一步会失败。
The default behavior of the database initializer is to unconditionally run the provided scripts. This may not always be what you want — for instance, if you run the scripts against a database that already has test data in it. The likelihood of accidentally deleting data is reduced by following the common pattern (shown earlier) of creating the tables first and then inserting the data. The first step fails if the tables already exist.
但是,为了对现有数据的创建和删除获得更多控制,XML 命名空间提供了一些额外的选项。第一个是开启和关闭初始化的标志。您可以根据环境设置它(例如,从系统属性或环境 bean 中获取布尔值)。以下示例从系统属性获取一个值:
However, to gain more control over the creation and deletion of existing data, the XML namespace provides a few additional options. The first is a flag to switch the initialization on and off. You can set this according to the environment (such as pulling a boolean value from system properties or from an environment bean). The following example gets a value from a system property:
<jdbc:initialize-database data-source="dataSource"
enabled="#{systemProperties.INITIALIZE_DATABASE}"> 1
<jdbc:script location="..."/>
</jdbc:initialize-database>
1 | Get the value for enabled from a system property called INITIALIZE_DATABASE . |
控制对现有数据执行操作的第二个选项是对失败更宽容。为此,您可以控制初始化器忽略脚本中运行的 SQL 中的某些错误的能力,如下例所示:
The second option to control what happens with existing data is to be more tolerant of failures. To this end, you can control the ability of the initializer to ignore certain errors in the SQL it runs from the scripts, as the following example shows:
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
<jdbc:script location="..."/>
</jdbc:initialize-database>
在上一个示例中,我们说我们期望有时在空数据库上运行脚本,并且该脚本中有一些`DROP`语句,因此会出现失败。因此,会忽略失败的 SQL DROP`语句,但其他失败会引发异常。如果您的 SQL 方言不支持`DROP … IF EXISTS
(或类似方言),但您想要在重新创建所有它之前无条件删除所有测试数据,则这将非常有用。在该情况下,第一个脚本通常是一组`DROP`语句,然后是一组`CREATE`语句。
In the preceding example, we are saying that we expect that, sometimes, the scripts are run
against an empty database, and there are some DROP
statements in the scripts that
would, therefore, fail. So failed SQL DROP
statements will be ignored, but other failures
will cause an exception. This is useful if your SQL dialect doesn’t support DROP … IF
EXISTS
(or similar) but you want to unconditionally remove all test data before
re-creating it. In that case the first script is usually a set of DROP
statements,
followed by a set of CREATE
statements.
ignore-failures`选项可以设置为`NONE
(默认值)、DROPS
(忽略失败的数据库删除)或`ALL`(忽略所有失败)。
The ignore-failures
option can be set to NONE
(the default), DROPS
(ignore failed
drops), or ALL
(ignore all failures).
如果脚本中根本没有`;`字符,则每个语句都应该用`;`或新行分开。您可以全局或逐个脚本控制它,如下所示:
Each statement should be separated by ;
or a new line if the ;
character is not
present at all in the script. You can control that globally or script by script, as the
following example shows:
<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 | Set the separator scripts to @@ . |
2 | Set the separator for db-schema.sql to ; . |
在这个示例中,两个`test-data`脚本使用`@@作为语句分隔符,而只有`db-schema.sql`使用
;`。此配置指定默认分隔符是`@@`,并覆盖`db-schema`脚本的默认设置。
In this example, the two test-data
scripts use @@
as statement separator and only
the db-schema.sql
uses ;
. This configuration specifies that the default separator
is @@
and overrides that default for the db-schema
script.
如果你需要比 XML 命名空间更多的控制,你可以直接使用 DataSourceInitializer
,并将其定义为应用程序中的组件。
If you need more control than you get from the XML namespace, you can use the
DataSourceInitializer
directly and define it as a component in your application.
Initialization of Other Components that Depend on the Database
大量的应用程序(那些在 Spring 上下文启动之后才使用数据库的应用程序)可以使用数据库初始化器,而不需要进一步复杂化。如果你的应用程序不是其中之一,你可能需要阅读本节的其余部分。
A large class of applications (those that do not use the database until after the Spring context has started) can use the database initializer with no further complications. If your application is not one of those, you might need to read the rest of this section.
数据库初始化器依赖于 DataSource
实例,并运行其初始化回调中提供的脚本(类似于 XML bean 定义中的 init-method
、组件中的 @PostConstruct
方法或实现 InitializingBean
的组件中的 afterPropertiesSet()
方法)。如果其他 bean 依赖于相同的数据源并在初始化回调中使用数据源,则可能会出现问题,因为数据尚未初始化。一个常见的示例是急切地初始化并从应用程序启动时的数据库加载数据的缓存。
The database initializer depends on a DataSource
instance and runs the scripts
provided in its initialization callback (analogous to an init-method
in an XML bean
definition, a @PostConstruct
method in a component, or the afterPropertiesSet()
method in a component that implements InitializingBean
). If other beans depend on the
same data source and use the data source in an initialization callback, there
might be a problem because the data has not yet been initialized. A common example of
this is a cache that initializes eagerly and loads data from the database on application
startup.
要解决此问题,你有两个选项:将缓存初始化策略更改为后面的阶段,或者确保数据库初始化器首先初始化。
To get around this issue, you have two options: change your cache initialization strategy to a later phase or ensure that the database initializer is initialized first.
如果你控制应用程序并且没有其他方式,则更改缓存初始化策略可能会很容易。以下列出了如何实现此目的的一些建议:
Changing your cache initialization strategy might be easy if the application is in your control and not otherwise. Some suggestions for how to implement this include:
-
Make the cache initialize lazily on first usage, which improves application startup time.
-
Have your cache or a separate component that initializes the cache implement
Lifecycle
orSmartLifecycle
. When the application context starts, you can automatically start aSmartLifecycle
by setting itsautoStartup
flag, and you can manually start aLifecycle
by callingConfigurableApplicationContext.start()
on the enclosing context. -
Use a Spring
ApplicationEvent
or similar custom observer mechanism to trigger the cache initialization.ContextRefreshedEvent
is always published by the context when it is ready for use (after all beans have been initialized), so that is often a useful hook (this is how theSmartLifecycle
works by default).
确保数据库初始化器首先初始化也很容易。以下列出了有关如何实现此目的的一些建议:
Ensuring that the database initializer is initialized first can also be easy. Some suggestions on how to implement this include:
-
Rely on the default behavior of the Spring
BeanFactory
, which is that beans are initialized in registration order. You can easily arrange that by adopting the common practice of a set of<import/>
elements in XML configuration that order your application modules and ensuring that the database and database initialization are listed first. -
Separate the
DataSource
and the business components that use it and control their startup order by putting them in separateApplicationContext
instances (for example, the parent context contains theDataSource
, and the child context contains the business components). This structure is common in Spring web applications but can be more generally applied.