You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot首次启动后重启失败:外键元数据解析异常

Spring Boot Hibernate外键元数据解析错误解决方案

问题背景

开发Spring Boot REST CRUD服务,包含UserMetadata和Organization两个实体,UserMetadata通过多对一关联引用Organization的外键。首次启动服务生成数据库表后,重启应用会抛出如下错误:

org.hibernate.tool.schema.spi.SchemaManagementException: Attempt to resolve foreign key metadata from JDBC metadata failed to find column mappings for foreign key named [FK_OrganizationToMetadata]

删除数据库表后可正常启动,但无法每次重启都删除表。移除org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl物理命名策略可解决问题,但希望保留该策略。

实体代码

Organization实体

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Data
@Entity
@Table(name = "Organizations-v2")
public class Organization
{
    // This UUID should come from the matching Keycloak Group
    @Id
    @Column(name="id", nullable=false)
    UUID id;
    @Column(nullable = false, columnDefinition = "text")
    String name;
    @Column(columnDefinition = "text")
    String address1;
    @Column(columnDefinition = "text")
    String address2;
    @Column(columnDefinition = "text")
    String stateProvince;
    @Column(columnDefinition = "text")
    String country;
    @Column(columnDefinition = "text")
    String city;
    Integer postalCode;
    @Column(columnDefinition = "text")
    String availableSoftwareProducts;
}

UserMetadata实体

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Data
@Entity
@Table(name = "UserMetadata")
public class UserMetadata
{
    @Id
    @Column(name = "user-id", nullable = false)
    UUID userID;
    @ManyToOne
    @JoinColumn(name = "org-id", referencedColumnName = "id", foreignKey=@ForeignKey(name = "FK_OrganizationToMetadata"))
    Organization organization;
    @Column(name = "user-product-args", columnDefinition = "text")
    String userProductArguments;
    @Column(name = "is-org-admin", nullable = false)
    boolean isOrgAdmin;
    
    public Map<String, String> getParsedUserProductArguments()
    {
        if(userProductArguments == null) return Map.of();
        
        Map<String, String> map = new HashMap<>();
        
        for(String str : userProductArguments.split(","))
        {
            String[] keyValue = str.split("=");
            if(keyValue.length == 2) map.put(keyValue[0], keyValue[1]);
        }
        
        return map;
    }
}

数据库表结构

  • Organizations-v2表:主键为id(UUID类型),包含name、address1等文本类型字段
  • UserMetadata表:主键为user-id(UUID类型),org-id为外键关联Organizations-v2的id列,外键名称为FK_OrganizationToMetadata

完整错误栈

2024-06-28T11:34:40.229-06:00 ERROR 23472 --- [cORE] [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Attempt to resolve foreign key metadata from JDBC metadata failed to find column mappings for foreign key named [FK_OrganizationToMetadata]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) ~[spring-context-6.1.10.jar:6.1.10]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.10.jar:6.1.10]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.1.jar:3.3.1]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.1.jar:3.3.1]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.1.jar:3.3.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.1.jar:3.3.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.1.jar:3.3.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.1.jar:3.3.1]
    at design.ore.cORE.Main.main(Main.java:37) ~[main/:na]
Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Attempt to resolve foreign key metadata from JDBC metadata failed to find column mappings for foreign key named [FK_OrganizationToMetadata]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421) ~[spring-orm-6.1.10.jar:6.1.10]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.10.jar:6.1.10]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) ~[spring-orm-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) ~[spring-beans-6.1.10.jar:6.1.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) ~[spring-beans-6.1.10.jar:6.1.10]
    ... 15 common frames omitted
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Attempt to resolve foreign key metadata from JDBC metadata failed to find column mappings for foreign key named [FK_OrganizationToMetadata]
    at org.hibernate.tool.schema.extract.internal.AbstractInformationExtractorImpl$ForeignKeyBuilderImpl.build(AbstractInformationExtractorImpl.java:1383) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.extract.internal.AbstractInformationExtractorImpl$ForeignKeyBuilderImpl.build(AbstractInformationExtractorImpl.java:1364) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.extract.internal.AbstractInformationExtractorImpl.getForeignKeys(AbstractInformationExtractorImpl.java:1348) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.extract.internal.TableInformationImpl.foreignKeys(TableInformationImpl.java:97) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.extract.internal.TableInformationImpl.getForeignKeys(TableInformationImpl.java:91) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.equivalentForeignKeyExistsInDatabase(AbstractSchemaMigrator.java:484) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.checkForExistingForeignKey(AbstractSchemaMigrator.java:476) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applyForeignKeys(AbstractSchemaMigrator.java:443) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:268) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:117) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:280) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at java.base/java.util.HashMap.forEach(HashMap.java:1429) ~[na:na]
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:322) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.10.jar:6.1.10]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) ~[spring-orm-6.1.10.jar:6.1.10]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.10.jar:6.1.10]
    ... 19 common frames omitted

解决方案

原因分析

PhysicalNamingStrategyStandardImpl会严格使用实体中定义的名称,但Organizations-v2表名包含减号这种特殊字符,Hibernate在解析JDBC元数据时,数据库可能需要用引号包裹特殊字符的表名才能正确识别,导致Hibernate无法匹配外键关联的表和列映射。

方案1:自定义物理命名策略,为特殊字符表名添加引号

继承PhysicalNamingStrategyStandardImpl,重写表名转换方法,对包含特殊字符的表名添加对应数据库的引号(比如MySQL用反引号,PostgreSQL用双引号):

package com.yourpackage;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

public class QuotedPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {
    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        Identifier original = super.toPhysicalTableName(name, context);
        // 检测表名是否包含特殊字符,这里以减号为例
        if (original.getText().contains("-")) {
            // 根据数据库类型选择引号,MySQL用`,PostgreSQL用"
            return Identifier.toIdentifier("`" + original.getText() + "`", original.isQuoted());
        }
        return original;
    }
}

在配置文件中替换物理命名策略:

# application.properties
spring.jpa.hibernate.naming.physical-strategy=com.yourpackage.QuotedPhysicalNamingStrategy

方案2:修改表名,移除特殊字符

将Organizations-v2改为Organizations_v2或者OrganizationsV2,避免使用减号这类特殊字符,这样Hibernate无需额外处理即可正常解析元数据:

@Table(name = "Organizations_v2")
public class Organization {
    // ... 其余代码不变
}

方案3:验证外键定义一致性

确认@JoinColumn的referencedColumnName与Organization实体的主键列名完全一致,同时检查数据库中外键FK_OrganizationToMetadata确实正确关联了UserMetadata.org-id和Organizations-v2.id。

内容的提问来源于stack exchange,提问作者Jacob Batista

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:05:54