Skip to content

初始化 DataSource (Initializing a DataSource)

org.springframework.jdbc.datasource.init 包提供了对现有 DataSource 进行初始化的支持。虽然内嵌数据库支持提供了一种创建和初始化 DataSource 的方式,但有时你可能需要初始化运行在远程服务器上的数据库实例。

使用 Spring XML 初始化数据库

如果你想初始化一个数据库,并且可以提供一个 DataSource Bean 的引用,可以使用 spring-jdbc 命名空间中的 initialize-database 标签:

xml
<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>

上述示例对数据库运行了两个指定的脚本。第一个脚本创建架构(Schema),第二个脚本向表中填充测试数据集。脚本位置也可以是包含通配符的 Ant 风格模式(例如 classpath*:/com/foo/**/sql/*-data.sql)。如果使用模式,脚本将按其 URL 或文件名的字典顺序运行。

控制初始化行为

默认情况下,数据库初始化器会无条件运行提供的脚本。为了获得更多控制,XML 命名空间提供了以下选项:

  • 启用开关:你可以根据环境(如系统属性)开启或关闭初始化。

    xml
    <jdbc:initialize-database data-source="dataSource"
    	enabled="#{systemProperties.INITIALIZE_DATABASE}">
    	<jdbc:script location="..."/>
    </jdbc:initialize-database>
  • 忽略错误:你可以控制初始化器忽略 SQL 运行中的某些错误。

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

    ignore-failures 的取值包括:NONE(默认)、DROPS(忽略失败的 DROP 语句)或 ALL(忽略所有失败)。

  • 分隔符控制:默认分隔符是 ;。你可以全局设置或针对特定脚本覆盖。

    xml
    <jdbc:initialize-database data-source="dataSource" separator="@@">
    	<jdbc:script location="classpath:schema.sql" separator=";"/>
    	<jdbc:script location="classpath:data.sql"/>
    </jdbc:initialize-database>

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

数据库初始化器是在 Spring 容器的初始化回调阶段运行的。如果你的其他 Bean(比如一个在启动时就加载数据的缓存组件)也依赖同一个 DataSource 并尝试在自己的初始化回调中使用它,可能会遇到数据尚未初始化的“先有鸡还是先有蛋”的问题。

解决方案:

  1. 延迟初始化缓存:将缓存改为在第一次使用时延迟加载,这还能提升应用启动速度。
  2. 使用 Lifecycle 机制:让缓存组件实现 LifecycleSmartLifecycleContextRefreshedEvent 事件是在所有 Bean 初始化完成后才发布的,这是一个很好的加载数据触发点。
  3. 调整 Bean 初始化顺序
    • 利用 Spring 默认的注册顺序(在 XML 中将数据库定义放在前面)。
    • 在 XML 中显式使用 depends-on 属性。

补充教学

1. 为什么不用 Flyway 或 Liquibase?

你可能会问:“Spring 提供的这个初始化功能和 Flyway/Liquibase 有什么区别?”

  • Spring JDBC 初始化器:非常简单、轻量。它就像是一个“一次性”的脚本执行器。它不具备版本管理能力。如果你第二次启动它,它会傻傻地再次运行 schema.sql,如果没写 IF NOT EXISTS,就会报错。
  • Flyway/Liquibase:是专业的“数据库版本管理工具”。它们会在数据库里建一张表记录哪些脚本执行过了,支持增量更新、回滚和多开发协作。

建议:在本地单元测试、临时演示环境,用 Spring 的 initialize-database 足矣。但在生产环境或团队协作项目中,请务必使用 Flyway 或 Liquibase。

2. Java 配置方式 (Java Config)

虽然文档给出了 XML 示例,但在现代 Spring 项目中,你更可能需要 Java 配置:

java
@Bean
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
    ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
    populator.addScript(new ClassPathResource("schema.sql"));
    populator.addScript(new ClassPathResource("data.sql"));
    populator.setContinueOnError(true); // 类似 ignore-failures

    DataSourceInitializer initializer = new DataSourceInitializer();
    initializer.setDataSource(dataSource);
    initializer.setDatabasePopulator(populator);
    return initializer;
}

3. 脚本编码问题

如果你的 SQL 脚本包含中文字符,务必指定编码,否则在某些操作系统上会产生乱码:

xml
<jdbc:script location="..." encoding="UTF-8"/>

或者在 Java 中:populator.setSqlScriptEncoding("UTF-8");

Based on Spring Framework.