初始化 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>上述示例对数据库运行了两个指定的脚本。第一个脚本创建架构(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 并尝试在自己的初始化回调中使用它,可能会遇到数据尚未初始化的“先有鸡还是先有蛋”的问题。
解决方案:
- 延迟初始化缓存:将缓存改为在第一次使用时延迟加载,这还能提升应用启动速度。
- 使用
Lifecycle机制:让缓存组件实现Lifecycle或SmartLifecycle。ContextRefreshedEvent事件是在所有 Bean 初始化完成后才发布的,这是一个很好的加载数据触发点。 - 调整 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 配置:
@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 脚本包含中文字符,务必指定编码,否则在某些操作系统上会产生乱码:
<jdbc:script location="..." encoding="UTF-8"/>或者在 Java 中:populator.setSqlScriptEncoding("UTF-8");。