使用HSQLDB的TDD - 删除外键

时间:2010-04-21 17:47:27

标签: hibernate spring tdd foreign-keys hsqldb

我正在使用HSQLDB进行数据层集成测试,这很棒。但是,我发现我的外键约束妨碍了我的测试。例如,要在一个表上测试一个简单的选择,我必须将虚拟数据插入另外五个表中。这让我想扔东西。

我在整个模型代码中都有JPA注释,并已配置Hibernate以在配置中重新创建模式(hbm2ddl.create-drop)。在生成表时,连接被正确解释为外键约束。

我想要的是:

  1. 最初不创建外键(理想,最干净)或
  2. 找到一种方法以编程方式删除数据库中的所有外键(有点hacky但会完成工作)
  3. 如果它有用,我正在使用Spring来自动测试这些测试。有问题的测试继承自AbstractTransactionalJUnit4SpringContextTests

    你怎么看?可以这样做吗?

5 个答案:

答案 0 :(得分:9)

您可以使用以下指令停用FK约束:

SET REFERENTIAL_INTEGRITY FALSE;

您可以在测试方法之前通过JDBC Statement执行它(并在之后将其设置回TRUE)。

答案 1 :(得分:8)

我在尝试使用平面xml数据集测试DAO时遇到了同样的问题。 Config是DBunit + HSQLDB 2.2.8 + JUnit4 + Spring + JPA->一共导致

java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; FK13EE6CE6F09A6AAC table: ****

我通过实现扩展AbstractTestExecutionListener的侦听器找到了一个很好的解决方法。您可以指定在每次测试之前要采取的操作类型,在我们的示例中,禁用外键约束。 注意:语法可能因使用的HSQLDB版本而异。

public class ForeignKeyDisabling extends AbstractTestExecutionListener {    
    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        IDatabaseConnection dbConn = new DatabaseDataSourceConnection(
                testContext.getApplicationContext().getBean(DataSource.class)
                );
        dbConn.getConnection().prepareStatement("SET DATABASE REFERENTIAL INTEGRITY FALSE").execute();

    }
}

然后,您只需要在测试中已经存在的集合中添加此侦听器:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"applicationContext-test.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DataSetTestExecutionListener.class, ForeignKeyDisabling.class})

答案 2 :(得分:2)

基于这个主题的灵感,我为这个问题创建了一些更强大的解决方案。关键是,我在运行测试时非常喜欢约束,所有其他解决方案都让它被禁用。此代码仅在数据集导入期间禁用它们,然后重新启用它们。并且可以轻松扩展以支持另一个数据库引擎:

import com.github.springtestdbunit.DbUnitTestExecutionListener;
import org.apache.log4j.Logger;
import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.springframework.test.context.TestContext;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Class DisableForeignKeysDbUnitTestExecutionListener
 * Simple wrapper class around DbUnitTestExecutionListener, which - for the time of importing the database -
 * disables Foreign Key Constraints checks.
 * This class can be extended by simply overriding toggleForeignKeysConstraintsForDbEngine(Connection, String, boolean);
 * subclasses should always call super-implementation for default case.
 */
public class DisableForeignKeysDbUnitTestExecutionListener
    extends DbUnitTestExecutionListener
{
    private static final Logger logger = Logger.getLogger(DisableForeignKeysDbUnitTestExecutionListener.class);
    private Connection cachedDbConnection;

    @Override
    public void beforeTestMethod(TestContext testContext)
        throws Exception
    {
        this.toggleForeignKeysConstraints(testContext, false);
        super.beforeTestMethod(testContext);
        this.toggleForeignKeysConstraints(testContext, true);
    }

    /**
     * Method should perform query to disable foreign keys constraints or return false,
     * if it is not able to perform such query (e.g. unknown database engine)
     *
     * @param connection    Database connection
     * @param dbProductName Name of the database product (as reported by connection metadata)
     * @param enabled       Expected state of foreign keys after the call
     *
     * @return True, if there was suitable statement for specified engine, otherwise false
     *
     * @throws SQLException
     */
    protected boolean toggleForeignKeysConstraintsForDbEngine(Connection connection, String dbProductName, boolean enabled)
        throws SQLException
    {
        switch (dbProductName)
        {
            case "HSQL Database Engine":
                connection.prepareStatement("SET DATABASE REFERENTIAL INTEGRITY " + (enabled ? "TRUE" : "FALSE"))
                          .execute();
                return (true);
        }
        return (false);
    }

    private void toggleForeignKeysConstraints(TestContext testContext, boolean enabled)
    {
        try
        {
            Connection connection = this.getDatabaseConnection(testContext);
            String databaseProductName = connection.getMetaData().getDatabaseProductName();
            if (!this.toggleForeignKeysConstraintsForDbEngine(connection, databaseProductName, enabled))
            {
                throw new IllegalStateException("Unknown database engine '" + databaseProductName +
                                                    "'. Unable to toggle foreign keys constraints.");
            }
        }
        catch (Throwable throwable)
        {
            logger.error("Unable to toggle Foreign keys constraints: " + throwable.getLocalizedMessage());
        }
    }

    synchronized private Connection getDatabaseConnection(TestContext testContext)
        throws SQLException
    {
        if (this.cachedDbConnection == null)
        {
            DataSource dataSource = testContext.getApplicationContext().getBean(DataSource.class);
            if (dataSource == null)
            {
                throw new IllegalStateException("Unable to obtain DataSource from ApplicationContext. " +
                                                    "Foreign constraints will not be disabled.");
            }

            IDatabaseConnection dsConnection = new DatabaseDataSourceConnection(dataSource);
            this.cachedDbConnection = dsConnection.getConnection();
        }

        return (this.cachedDbConnection);
    }
}

答案 3 :(得分:0)

我会考虑花一些时间来创建几个灯具,可能还有DBUnit,你可以插入@Before。

BTW,AbstractTransactionalJUnit4Test在Spring 3.0中已弃用

答案 4 :(得分:0)

快速做到:

SET REFERENTIAL_INTEGRITY FALSE;到您的测试资源目录中的import.sql文件中。

问题解决得很快很好:)

以下是有关import.sql http://christopherlakey.com/articles/import-sql.html

的一些信息