Hibernate/H2 外键抛出 org.h2.jdbc.JdbcSQLException

Posted

技术标签:

【中文标题】Hibernate/H2 外键抛出 org.h2.jdbc.JdbcSQLException【英文标题】:Hibernate/H2 foreign key throwing org.h2.jdbc.JdbcSQLException 【发布时间】:2012-09-11 14:03:20 【问题描述】:

我正在尝试使用 H2 数据库进行单元测试。我的实际数据库是 mysql,所以我将 MYSQL 方言与 H2 一起使用。一旦 hbm 文件被读取/加载,问题似乎就会发生。当 H2 或 Hibernate 尝试更改表时,外键/关联会出现问题。或者看起来是这样。

数据库脚本:

DROP SCHEMA IF EXISTS `amr` ;
CREATE SCHEMA IF NOT EXISTS `amr`;

-- -----------------------------------------------------
-- Table `amr`.`classA`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `amr`.`classA` ;CREATE  TABLE IF NOT EXISTS `amr`.`classA` (
  `classA_id` INT(11) NOT NULL ,
  `name` VARCHAR(56) NOT NULL ,
  PRIMARY KEY (`classA_id`) );

-- -----------------------------------------------------
-- Table `amr`.`classB`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `amr`.`classB` ;CREATE  TABLE IF NOT EXISTS `amr`.`classB` (
  `classB_id` INT(11) NOT NULL ,
  `name` VARCHAR(45) NOT NULL ,
  `classA_id` INT(11) NOT NULL ,
  PRIMARY KEY (`classB_id`) ,
  INDEX `fk_classB_service_provider_classA1` (`classA_id` ASC) ,
  CONSTRAINT `fk_classB_service_provider_classA1`
    FOREIGN KEY (`classA_id` )
    REFERENCES `amr`.`classA` (`classA_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION);

我收到的异常:

09-11 08:46:33 jdbc[2]: exception
org.h2.jdbc.JdbcSQLException: Column "FOREIGN" not found; SQL statement:
alter table amr.classB drop foreign key FKAF0F85CABD96B12B [42122-168]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
    at org.h2.message.DbException.get(DbException.java:169)
    at org.h2.message.DbException.get(DbException.java:146)
    at org.h2.table.Table.getColumn(Table.java:605)
    at org.h2.command.Parser.parseAlterTable(Parser.java:4874)
    at org.h2.command.Parser.parseAlter(Parser.java:4315)
    at org.h2.command.Parser.parsePrepared(Parser.java:306)
    at org.h2.command.Parser.parse(Parser.java:279)
    at org.h2.command.Parser.parse(Parser.java:251)
    at org.h2.command.Parser.prepareCommand(Parser.java:217)
    at org.h2.engine.Session.prepareLocal(Session.java:415)
    at org.h2.engine.Session.prepareCommand(Session.java:364)
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1109)
    at org.h2.jdbc.JdbcStatement.executeUpdateInternal(JdbcStatement.java:121)
    at org.h2.jdbc.JdbcStatement.executeUpdate(JdbcStatement.java:110)
    at org.hibernate.tool.hbm2ddl.SchemaExport.execute(SchemaExport.java:421)
    at org.hibernate.tool.hbm2ddl.SchemaExport.drop(SchemaExport.java:396)
    at org.hibernate.tool.hbm2ddl.SchemaExport.execute(SchemaExport.java:269)
    at org.hibernate.tool.hbm2ddl.SchemaExport.create(SchemaExport.java:219)
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:370)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1842)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:860)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:779)
    at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:188)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:567)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:103)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1)
    at org.springframework.test.context.support.DelegatingSmartContextLoader.loadContext(DelegatingSmartContextLoader.java:228)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:290)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
09-11 08:46:33 jdbc[2]: exception
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "ALTER TABLE AMR.CLASSB ADD INDEX FKAF0F85CABD96B12B (CLASSA_ID),[*] ADD CONSTRAINT FKAF0F85CABD96B12B FOREIGN KEY (CLASSA_ID) REFERENCES AMR.CLASSA (ID) "; SQL statement:
alter table amr.classB add index FKAF0F85CABD96B12B (classA_id), add constraint FKAF0F85CABD96B12B foreign key (classA_id) references amr.classA (id) [42000-168]
09-11 08:46:34 jdbc[2]: exception
org.h2.jdbc.JdbcSQLException: Column "FOREIGN" not found; SQL statement:
alter table amr.classB drop foreign key FKAF0F85CABD96B12B [42122-168]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
    at org.h2.message.DbException.get(DbException.java:169)
    at org.h2.message.DbException.get(DbException.java:146)
    at org.h2.table.Table.getColumn(Table.java:605)
    at org.h2.command.Parser.parseAlterTable(Parser.java:4874)
    at org.h2.command.Parser.parseAlter(Parser.java:4315)
    at org.h2.command.Parser.parsePrepared(Parser.java:306)
    at org.h2.command.Parser.parse(Parser.java:279)
    at org.h2.command.Parser.parse(Parser.java:251)
    at org.h2.command.Parser.prepareCommand(Parser.java:217)
    at org.h2.engine.Session.prepareLocal(Session.java:415)
    at org.h2.engine.Session.prepareCommand(Session.java:364)
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1109)
    at org.h2.jdbc.JdbcStatement.executeUpdateInternal(JdbcStatement.java:121)
    at org.h2.jdbc.JdbcStatement.executeUpdate(JdbcStatement.java:110)
    at org.hibernate.tool.hbm2ddl.SchemaExport.execute(SchemaExport.java:421)
    at org.hibernate.tool.hbm2ddl.SchemaExport.drop(SchemaExport.java:396)
    at org.hibernate.tool.hbm2ddl.SchemaExport.execute(SchemaExport.java:269)
    at org.hibernate.tool.hbm2ddl.SchemaExport.drop(SchemaExport.java:229)
    at org.hibernate.impl.SessionFactoryImpl.close(SessionFactoryImpl.java:959)
    at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.destroy(AbstractSessionFactoryBean.java:228)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.destroy(LocalSessionFactoryBean.java:899)
    at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:211)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:498)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:474)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:442)
    at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1066)
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1040)
    at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:958)

hbm 映射:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
    <class name="ClassA" table="classA">
        <id name="id" type="long">
            <column name="id"/>
        </id>
        <property name="name" type="string">
            <column name="name" length="56" not-null="true"/>
        </property>
        <set name="classBs" table="classB" inverse="true" lazy="true" fetch="select">
            <key>
                <column name="classA_id" not-null="true"/>
            </key>
            <one-to-many class="ClassB"/>
        </set>
    </class>
</hibernate-mapping>

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
    <class name="ClassB" table="classB">
        <id name="id" type="long">
            <column name="id"/>
        </id>
        <property name="name" type="string">
            <column name="name" length="56" not-null="true"/>
        </property>
        <many-to-one name="classA" class="ClassA" fetch="select">
            <column name="classA_id" not-null="true"/>
        </many-to-one>
    </class>
</hibernate-mapping>

H2 网址:

jdbc:h2:~/db/amr_test;MODE=MYSQL;INIT=create schema if not exists test_db\\;runscript from 'classpath:amr_test_ddl.sql';DB_CLOSE_ON_EXIT=FALSE

-------编辑/发现问题------------ ----------

在几乎从头开始并重新评估所有内容之后,发现我不正确地将以下属性添加到我的上下文文件中:

创建删除

在指导我的 H2 上下文配置的论坛之一中指出使用它,因为它“在会话关闭后删除架构”。

所以我对这个属性的无知导致了一个教训。

@Thomas Mueller - 非常感谢您在这件事上的时间和帮助。

【问题讨论】:

如果您使用 H2 方言(并删除 MODE=MYSQL),它是否有效?我想你无论如何都需要有两种 Hibernate 配置(一种用于测试,一种用于生产)。 Hibernate 应该能够抽象出特定于数据库的特性,因此如果您在生产环境中使用不同的数据库,则无需过多关注。 当我更改为 h2 方言时,它对我的​​ ddl 语法不满意:org.h2.jdbc.JdbcSQLException: Unknown data type: "FK_CLASSB_SERVICE_PROVIDER_CLASSA1"; SQL 语句: CREATE TABLE IF NOT EXISTS amr.classB ( classB_id INT(11) NOT NULL , name VARCHAR(45) NOT NULL , classA_id INT(11) NOT NULL , PRIMARY KEY (@ 987654330@) , 索引 fk_classB_service_provider_classA1 (classA_id ASC) , 约束 fk_classB_service_provider_classA1 外键 (classA_id ) 引用 amr.classA (classA_id) 不删除操作跨度> 是的,我有单独的休眠配置,但试图维护一组数据库脚本文件 恐怕 H2 和 MySQL 不是 100% 兼容的。您可以更改 ddl 脚本,以便在创建表后使用更标准的 SQL 语句 (ALTER TABLE) 创建索引和外键。 是的,我之前尝试过这样做,但收到了同样的异常。这是我用于 B 类的 ddl: -- ---------------------------------------- -------------- -- 表amr.classB -- ------------ ----------------------------- 如果存在amr.classB,则删除表;如果不存在amr,则创建表。 classB ( classB_id INT(11) NOT NULL , name VARCHAR(45) NOT NULL , classA_id INT(11) NOT NULL , 主键 (classB_id) );更改表 amr.classB 添加外键 (classA_id) 引用 amr.classA(classA_id) 【参考方案1】:

您使用了仅适用于 MySQL 的脚本。 以下脚本应该是跨数据库的,根据我的测试它适用于 H2、MySQL 和 PostgreSQL(我没有测试其他数据库)。我还将 MySQL 特定的 INT(11) 替换为 INT。该脚本假定架构/数据库amr 已经存在:

drop table if exists amr.classB;
drop table if exists amr.classA;
CREATE TABLE IF NOT EXISTS amr.classA (
    classA_id INT(11) NOT NULL,
    PRIMARY KEY (classA_id)
);
CREATE TABLE IF NOT EXISTS amr.classB ( 
    classB_id INT(11) NOT NULL , 
    name VARCHAR(45) NOT NULL , 
    classA_id INT(11) NOT NULL , 
    PRIMARY KEY (classB_id)
);
CREATE INDEX fk_classB_service_provider_classA1 
    ON amr.classA(classA_id ASC);
ALTER TABLE amr.classB 
    ADD CONSTRAINT fk_classB_service_provider_classA1 
    FOREIGN KEY (classA_id) 
    REFERENCES amr.classA(classA_id)
    ON DELETE NO ACTION ON UPDATE NO ACTION;

然后,在 Hibernate 配置中,为 H2 使用 H2 模式。

【讨论】:

以上是关于Hibernate/H2 外键抛出 org.h2.jdbc.JdbcSQLException的主要内容,如果未能解决你的问题,请参考以下文章

Javasist 在检测 org.h2.jdbc.JdbcPreparedStatement 的 setString 方法时抛出 javassist.CannotCompileException

调试 JHipster 应用程序抛出:java.lang.ClassNotFoundException: org.h2.server.web.WebServlet

java需要h2数据库抛出jdbc异常

具有正确 DDL sql 的 H2 org.h2.jdbc.JdbcSQLException:错误代码 = [42000-196]

h2 数据库的 ClassNotFound 异常 class.forname("org.h2.Driver")

删除约束后 h2 删除列失败