【问题标题】:Configuring HSQL Server for Hibernate tests为 Hibernate 测试配置 HSQL Server
【发布时间】:2012-11-02 06:57:18
【问题描述】:

我刚刚开始使用 Hibernate 并开始思考问题。

目前我正在尝试设置一个测试环境,我可以在其中使用 HSQL 内存实例来测试我的项目。

我遇到的错误是:

javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: invalid schema name: TSG

以下是我项目的相关部分:

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>

org.hibernate.ejb.HibernatePersistence com.foo.api.models.tsg.AlgPpcAlgorithmOutputEntity

<persistence-unit name="TestingPersistenceUnit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>com.foo.api.models.tsg.AlgPpcAlgorithmOutputEntity
    </class>
    <properties>
        <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/>
        <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
        <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:tsg"/>
        <property name="hbm2ddl.auto" value="create-drop"/>
        <property name="hibernate.connection.autocommit" value="true"/>
        <property name="hibernate.connection.username" value="sa"/>
        <property name="hibernate.connection.password" value=""/>
        <property name="hibernate.show_sql" value="true"/>
    </properties>
</persistence-unit>

如您所见,我有一个 peristence-unit 用于生产(工作正常)和一个内存 HSQL 一个用于测试(我无法开始工作)。

Hibernate 实体示例:

package com.foo.api.models.tsg;

import javax.persistence.*;
import java.math.BigDecimal;

@IdClass(AlgPpcAlgorithmOutputEntityPK.class)
@Table(name = "alg_ppc_algorithm_output", schema = "", catalog = "tsg")
@Entity
public class AlgPpcAlgorithmOutputEntity {
    private int parameterId;

    @Column(name = "parameter_id")
    @Id
    public int getParameterId() {
        return parameterId;
    }

    public void setParameterId(int parameterId) {
        this.parameterId = parameterId;
    }

    private String matchType;

    @Column(name = "matchType")
    @Basic
    public String getMatchType() {
        return matchType;
    }

    // for brevity I have removed the rest of the implementation
    // It was auto-generated by Hibernate and works fine in production.
}

最后是一个简单的TestCase类:

package tests.integration;


import com.foo.api.models.tsg.AlgPpcAlgorithmOutputEntity;
import com.foo.api.util.HibernateUtil;
import org.hsqldb.Server;
import org.hsqldb.persist.HsqlProperties;
import org.hsqldb.server.ServerConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tests.util.HSQLServerUtil;

import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityManager;
import javax.persistence.Persistence;

import com.foo.api.KeywordManager;

import java.sql.Date;
import java.util.HashSet;

public class KeywordManagerTestCase {

    private static final Logger LOG =   LoggerFactory.getLogger(KeywordManagerTestCase.class);
    private EntityManagerFactory eMF;
    protected EntityManager eM;

    @Before
    public void setUp() throws Exception {

        HsqlProperties props = new HsqlProperties();
        props.setProperty("server.database.0", "mem:tsg");
        props.setProperty("server.dbname.0", "tsg");

        ServerConfiguration.translateDefaultDatabaseProperty(props);

        Server hsqlServer = new Server();
        hsqlServer.setRestartOnShutdown(false);
        hsqlServer.setNoSystemExit(true);
        hsqlServer.setProperties(props);
        hsqlServer.setTrace(true);

        LOG.info("Configured the HSQLDB server...");
        hsqlServer.start();
        LOG.info("HSQLDB server started on port " + hsqlServer.getPort() + "...");

        LOG.info("Loading hibernate...");
        if (eMF == null) {
            eMF = Persistence.createEntityManagerFactory("TestingPersistenceUnit");
        }

        eM = eMF.createEntityManager();
    }

    /**
     * shutdown the server.
     * @throws Exception in case of errors.
     */
    @After
    public void tearDown() throws Exception {
        eM.close();
        HSQLServerUtil.getInstance().stop();
    }

    /**
     * Demo test to see that the number of user records in the database corresponds the flat file inserts.
     */
    @Test
    public void testDemo1() {
        AlgPpcAlgorithmOutputEntity entity = new AlgPpcAlgorithmOutputEntity();
        entity.setParameterId(200);
        entity.setMatchType("aa");
        KeywordManager km;
        eM.persist(entity);

        HashSet<Integer> params = new HashSet<Integer>();
        params.add(200);
        km = new KeywordManager(eM, params, new Date[2]);

        HashSet<AlgPpcAlgorithmOutputEntity> res = km.pullKeywords(params);
        for (AlgPpcAlgorithmOutputEntity s : res) {
            System.out.println(s.getMatchType());
        }

    }


}

我确定我以一种奇怪的方式设置了一些东西,但正如我所说 - 这是我的第一次尝试。

这是我想要做的:

  • 在 persistence.xml 中包含 HSQL 数据库的测试配置(以及所有休眠类映射)
  • 为单元测试启动 HSQL db,并使用我的项目架构创建内存数据库(如类元素下的 persistence.xml 中所述)。
  • 创建实体对象并在测试时将它们添加到测试数据库,以便我可以将数据库用于我的集成测试。

我就是无法通过这个 PersistenceException!

更新

好的,所以我意识到我不需要在设置测试用例时设置显式 HSQL 服务器,因为这正是我的 persistence.xml 中的 persistence-unit 条目的用途。我还尝试更改 CATALOG 以使其与我的映射类使用的目录相匹配。我的测试用例的设置现在看起来像:

private EntityManagerFactory eMF;
protected EntityManager eM;

@Before
public void setUp() throws Exception {
    LOG.info("Loading hibernate...");
    if (eMF == null) {
        eMF = Persistence.createEntityManagerFactory("TestingPersistenceUnit");
    }

    eM = eMF.createEntityManager();

    EntityTransaction eT = null;
    eT = eM.getTransaction();
    eT.begin();
    Query q = eM.createNativeQuery("ALTER CATALOG PUBLIC RENAME TO TSG");
    q.executeUpdate();
    eT.commit();

    // And also it seems I need to create the schema
    eT = eM.getTransaction();
    eT.begin();
    q = eM.createNativeQuery("CREATE SCHEMA TSG AUTHORIZATION DBA");
    q.executeUpdate();
    eT.commit();
}

但是,我现在遇到了一个新错误,具体来说:

javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: user lacks privilege or object not found: ALG_PPC_ALGORITHM_OUTPUT

所以我到了某个地方,但似乎没有创建表。我想知道我的persistence.xml 是否有问题?

【问题讨论】:

  • 你的数据库文件名是什么? tsg?
  • @YogendraSingh 我想使用内存数据库,所以我不希望有数据库文件。但是,在生产环境(远程 mysql 服务器)上,数据库的 nametsg。我指定的内容是否意味着我无意中尝试使用基于文件的数据库?

标签: java database hibernate unit-testing hsqldb


【解决方案1】:

据我所知(我不是 HSQL 专家),您在连接 url 中指定的TSG 是数据库名称,与目录不同。见http://hsqldb.org/doc/2.0/guide/databaseobjects-chapt.html#dbc_schemas_schema_objects

在 HyperSQL 中,每个数据库只有一个目录。目录的名称是 PUBLIC。您可以使用 ALTER CATALOG RENAME TO 语句重命名目录。所有模式都属于此目录。目录名与数据库文件名无关。

正如我所读到的,当 HSQL 创建数据库时,它会在该数据库中创建一个名为 PUBLIC 的目录,并在该目录中创建一个名为 PUBLIC 的模式。每个 HSQL 数据库只能有一个目录。单个目录中可以有多个模式。

您遇到的错误实际上来自尝试在映射中指定catalog = "tsg"。该目录不存在。由于 HSQL 数据库只能包含一个目录,因此您必须将该 PUBLIC 目录重命名为 TSG(或更改您的映射)。

【讨论】:

  • 这是正确的。除非您重命名 PUBLIC 目录,否则没有 TSG 目录。
  • 您好,感谢您的回答。我试图取得进展并更新了我的问题。恐怕还是卡住了。
  • 为什么要重命名架构?应该没关系。您的映射仅命名了一个目录。 Hibernate 为@Table(name = "alg_ppc_algorithm_output", schema = "", catalog = "tsg") 提供的“对象名称”是什么?应该是tsg..alg_ppc_algorithm_output。这就是你看到的吗?
【解决方案2】:

您在 URL 中指定服务器名称,但尝试使用内存数据库,这导致了问题。

尝试使用 DB URL:

           jdbc:hsqldb:mem:tsg

  <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:tsg"/>

还可以使用ALTER CATALOG RENAME TO tsg 更改默认目录名称(PUBLIC)。

编辑:要自动创建架构,请在下面的 persistence.xml 中更新(hibernate.hbm2ddl.auto 代替 hbm2ddl.auto)

    <property name="hibernate.hbm2ddl.auto" value="create-drop"/>

【讨论】:

  • @Edwardr:将persistence.xml 中的密钥名称更新为hibernate.hbm2ddl.auto,即&lt;property name="hibernate.hbm2ddl.auto" value="create-drop"/&gt;
  • 是的,我大约在 15 分钟前就知道了!多么烦人的错字!无论如何,这取得了进展,但现在的情况是表是在 setup() 运行之前创建的,因此它们处于错误的模式等。但是,如果我使用像 Derby 这样的东西,一切都会完美,所以我可能会这样做做。再次感谢!
【解决方案3】:

您可以通过将 orm.xml 文件放入 src/test/resources/META-INF 来覆盖单元测试的实体映射(如果使用 maven 布局)。在此您可以覆盖 JPA 注释映射。根据您的需要,您只需要覆盖表格位置,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">

  <entity class="com.foo.api.models.tsg.AlgPpcAlgorithmOutputEntity">
    <table name="alg_ppc_algorithm_output"/>
  </entity>

</entity-mappings>

这会将您的表放入 hsqldb 数据库的默认公共目录中,因此您不必更改 hsqldb 方案。当您的单元测试使用包含同名表的多个目录时,它甚至可以工作,因为您只需在表名属性中提供不同的名称。

【讨论】:

  • 搞定了。但是有一个奇怪的问题;如果我通过 Eclipse 调用 test,orm.xml 会被 entitymanager 拾取,但如果我运行调用 mvn clean install orm.xml 则不会使用。在查看调试后,我注意到问题是由于在 org.hibernate.ejb.Ejb3Configuration 中使用了“root url”。在 Eclipse 的情况下,根 url 指的是找到 Meta-inf/orm.xml 的 test-classes 目录。在 mvn clean install 的情况下,根 url 是指 domain.jar,由单独的模块生成(项目具有域和存储库层)。由于我不能在域 jar 中包含测试 orm.xml,有没有办法解决这个问题?
【解决方案4】:

设置中有明显错误。连接 URL 必须指向服务器:

<property name="hibernate.connection.url" value="jdbc:hsqldb:hsql://localhost/tsg"/>

【讨论】:

  • 您好,我正在尝试连接到 in-memory 数据库。根据这些文档:hsqldb.org/doc/2.0/guide/dbproperties-chapt.html 不需要指定本地主机吗?
  • 在这种情况下,您在测试 setup() 方法中启动的服务器未被使用。
  • 好吧,我像个白痴一样意识到我在复制服务器配置。我的 persistence.xml 中概述的持久性单元正在为我处理服务器启动/停止,对吗? Persistence.createEntityManagerFactory("TestingPersistenceUnit"); 行足以让服务器启动并运行,对吗?
  • ALTER 语句应该是ALTER CATALOG PUBLIC RENAME TO TSG
  • 糟糕。所以我已经排序了,而且我也在创建模式。现在只是看看为什么没有创建表。我认为这一切都应该由 Hibernate 处理!
猜你喜欢
  • 2012-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-07
  • 2018-02-11
  • 1970-01-01
相关资源
最近更新 更多