Spring Boot 3.1.x中columnDefinition重复执行:是Bug还是设计?
Spring Boot 3.1.1中@Column columnDefinition重复执行导致DDL语法错误问题
问题场景
定义包含columnDefinition属性的JPA实体类后,首次运行能正常创建表;但未修改任何代码进行第二次运行时,系统尝试执行错误的ALTER语句,因语法错误失败。该问题仅出现在Spring Boot 3.1.1版本,Spring Boot 3.0.x及2.6.x版本无此异常。
实体类代码
@Entity @Table(name = "simple_user") public class SimpleUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(columnDefinition = "varchar(64) NOT NULL default 'John'") private String nickname; }
首次运行成功建表的SQL日志
2023-06-24 18:22:20.380 | INFO | restartedMain | p6spy | 21 ms | statement | create table simple_user (id serial not null, nickname varchar(64) NOT NULL default 'John', primary key (id)) | logSQL(Slf4JLogger.java:60) 2023-06-24 18:22:20.383 | INFO | restartedMain | p6spy | 2 ms | statement | comment on table simple_user is 'User' | logSQL(Slf4JLogger.java:60) 2023-06-24 18:22:20.387 | INFO | restartedMain | p6spy | 2 ms | statement | comment on column simple_user.nickname is 'nickname' | logSQL(Slf4JLogger.java:60)
第二次运行的错误日志
2023-06-24 18:25:25.520 | INFO | restartedMain | c.z.h.HikariDataSource | HikariPool-1 - Start completed. | getConnection(HikariDataSource.java:123) 2023-06-24 18:25:26.079 | INFO | restartedMain | p6spy | 22 ms | statement | select * from information_schema.sequences | logSQL(Slf4JLogger.java:60) 2023-06-24 18:25:26.106 | INFO | restartedMain | o.h.b.i.BytecodeProviderInitiator | HHH000021: Bytecode provider name : bytebuddy | buildBytecodeProvider(BytecodeProviderInitiator.java:53) 2023-06-24 18:25:27.239 | INFO | restartedMain | o.h.e.t.j.p.i.JtaPlatformInitiator | HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] | initiateService(JtaPlatformInitiator.java:51) 2023-06-24 18:25:27.272 | INFO | restartedMain | p6spy | 3 ms | statement | select * from information_schema.sequences | logSQL(Slf4JLogger.java:60) 2023-06-24 18:25:27.315 | INFO | restartedMain | p6spy | 5 ms | statement | alter table if exists simple_user alter column nickname set data type varchar(64) NOT NULL default 'John' | logSQL(Slf4JLogger.java:60) 2023-06-24 18:25:27.317 | WARN | restartedMain | o.h.t.s.i.ExceptionHandlerLoggedImpl | GenerationTarget encountered exception accepting command : Error executing DDL "alter table if exists simple_user alter column nickname set data type varchar(64) NOT NULL default 'John'" via JDBC [ERROR: syntax error at or near "NOT" Position: 83] | handleException(ExceptionHandlerLoggedImpl.java:27) org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "alter table if exists simple_user alter column nickname set data type varchar(64) NOT NULL default 'John'" via JDBC [ERROR: syntax error at or near "NOT" Position: 83] at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:92) at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigrator.java:574) at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigrator.java:514) at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.migrateTable(AbstractSchemaMigrator.java:333) at org.hibernate.tool.schema.internal.GroupedSchemaMigratorImpl.performTablesMigration(GroupedSchemaMigratorImpl.java:84) at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:232) at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:117)
问题分析与解决方案
是Bug还是设计?
这是Spring Boot 3.1.1(对应Hibernate版本升级)引入的Bug。原因是Hibernate在对比数据库元数据与实体定义时,错误地将columnDefinition中的约束(NOT NULL、default)包含到ALTER TABLE的set data type语句中,而标准SQL(尤其是PostgreSQL)修改数据类型的语法不允许在类型后直接附加约束,最终导致语法错误。旧版本Hibernate不会生成这类错误的ALTER语句。
如何禁止columnDefinition重复执行?
有以下几种可行方案:
调整DDL自动更新策略
- 将
spring.jpa.hibernate.ddl-auto设置为none或validate,关闭自动DDL修改能力(适合生产环境):spring.jpa.hibernate.ddl-auto=none - 若需保留首次自动建表能力,可在表创建完成后改为
validate,仅验证结构不执行修改。
- 将
拆分columnDefinition为JPA标准属性
- 避免在
columnDefinition中混合写约束,改用JPA标准注解属性替代:// 拆分约束为标准属性 @Column(length = 64, nullable = false, columnDefinition = "varchar(64) default 'John'") // 进一步拆分默认值(JPA 2.1+支持@ColumnDefault) @Column(length = 64, nullable = false) @ColumnDefault("'John'") private String nickname;
这种方式让Hibernate能正确识别约束,不会生成错误的ALTER语句。
- 避免在
升级Spring Boot版本
- 该问题已在Spring Boot 3.1.2及后续版本中修复,直接升级版本即可解决。
内容的提问来源于stack exchange,提问作者Heromyth
相关产品推荐
相关产品推荐

