Spring Boot启动初始化SQL脚本报错:表GEOGRAPHIC_ZONE未找到
我来帮你分析一下这个看似矛盾的问题——明明日志显示表已经创建了,但执行插入脚本时却提示表找不到,大概率是两个原因之一,或者两者结合导致的:
1. 表名大小写不匹配(最可能的原因)
你在数据源URL里设置了DATABASE_TO_UPPER=false,这会让H2数据库严格区分表名的大小写。从你的日志能看到,Hibernate实际创建的是小写的geographic_zone表,但你的SQL脚本里写的是插入到大写的GEOGRAPHIC_ZONE,这时候H2会认为这是两个不同的表,自然找不到。
为什么Hibernate创建小写表?默认情况下Hibernate的物理命名策略会把实体类里的大写表名转成小写(或小写加下划线),即使你在@Table里指定了大写的名字,也会被自动转换。
解决方法:
- 直接修改SQL脚本的表名为小写:
INSERT INTO geographic_zone (name) VALUES ('EUROPE'); - 或者在SQL脚本里用双引号包裹大写表名(H2区分大小写时需要这样):
INSERT INTO "GEOGRAPHIC_ZONE" (name) VALUES ('EUROPE'); - 也可以配置Hibernate的命名策略,让它严格按照你指定的表名创建:
在application.yml里添加:
这样spring: jpa: hibernate: naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl@Table(name = "GEOGRAPHIC_ZONE")就会创建大写的表,脚本里的语句就能匹配上了。
2. 初始化顺序颠倒(日志欺骗了你)
有时候日志的打印顺序并不代表实际代码的执行顺序,Hibernate的建表日志可能是异步输出的,而实际上Spring执行data.sql的时机比Hibernate建表更早。
Spring Boot默认情况下,data.sql是在数据源初始化完成后立刻执行的,而Hibernate的DDL操作是在EntityManagerFactory初始化时才执行的,这就会导致脚本执行时表还没被创建。
解决方法:
如果你用的是Spring Boot 2.5及以上版本,可以添加这个配置来延迟数据源初始化脚本的执行,让它在Hibernate建表之后运行:
spring: sql: init: mode: always jpa: defer-datasource-initialization: true
另外,你配置里的jpa.generate-ddl: true和hibernate.ddl-auto: update功能有重叠,建议去掉generate-ddl,只保留ddl-auto: update即可,避免配置冲突导致的顺序问题。
总结
优先检查表名大小写的问题,这是最常见的原因;如果确认大小写没问题,再调整初始化顺序的配置。
内容的提问来源于stack exchange,提问作者Spialdor

