如何防止 Hibernate 初始化时删除其他 schema 的数据?
结论
最可能的原因是 spring.jpa.hibernate.ddl-auto=create。Hibernate 会根据当前 EntityManagerFactory 管理的实体初始化表结构,通常先删除这些实体对应的表,再重新创建。如果 schema B 的实体也由同一个 EntityManagerFactory 管理,schema B 的表也可能被删除重建,导致数据丢失。
@Immutable 等 ORM 层面的只读设置无法限制 Hibernate 执行建表或删表操作。spring.sql.init.data-locations 用来指定数据初始化脚本。如果脚本中没有删除语句,应优先检查 Hibernate 启动时执行的 DDL。
先确认是哪一步删除了数据
查看应用启动日志中的 SQL,确认是否有针对 schema B 表的 DROP TABLE、CREATE TABLE 或 TRUNCATE。也可以检查数据库审计日志,确认执行操作的数据库账户和对应语句。如果没有发现 Hibernate 执行相关 DDL,再检查其他初始化脚本、触发器或应用启动代码。
保护 schema B 的做法
如果应用不需要 Hibernate 在启动时管理表结构,可以关闭自动 DDL,并通过迁移工具或人工迁移管理 schema A:
spring.jpa.hibernate.ddl-auto=validate
spring.sql.init.mode=always
spring.sql.init.data-locations=classpath:/sample.sql
spring.jpa.defer-datasource-initialization=true
validate 会检查实体映射是否与现有表结构匹配,但不会创建或更新表。如果不需要在启动时校验,可以设为 none。spring.jpa.generate-ddl=true 的用途与 Hibernate DDL 自动管理设置重叠。采用显式迁移时,建议移除该配置,避免配置意图不清。
如果需要在启动时自动处理 schema A,同时应用也映射了 schema B 的实体,应将两组实体配置到不同的持久化单元和 EntityManagerFactory。负责自动 DDL 的单元只管理 schema A 的实体,schema B 的实体则由另一个不执行自动 DDL 的单元管理。具体配置取决于项目使用的 Spring Boot、Hibernate 版本和数据源设置。
也可以考虑使用 Hibernate 提供的 schema 过滤扩展,但接口和配置方式会随 Hibernate 版本变化。使用前应核对项目的实际版本,并验证启动时不会对 schema B 的表执行 DDL。仅靠实体只读注解无法实现这一限制。
额外保护
如果 schema B 由另一个应用管理,可以为当前应用配置权限受限的数据库账户,不授予该账户删除表、创建表或删除数据的权限。这样即使配置误触发结构操作,数据库也会拒绝未授权操作。不过,Hibernate 的启动过程可能因此报错,所以仍需正确限定自动 DDL 的管理范围。
备注:内容仅供参考。
