12

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

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

我想要的是:

  1. 最初不创建外键(理想,最干净),或
  2. 找到一种以编程方式删除数据库中所有外键的方法(有点 hacky 但会完成工作)

如果有帮助,我将使用 Spring 自动装配这些测试。有问题的测试继承自AbstractTransactionalJUnit4SpringContextTests

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

4

5 回答 5

11

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

SET REFERENTIAL_INTEGRITY FALSE;

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

于 2010-04-21T21:04:41.587 回答
10

我在尝试使用平面 xml 数据集测试我的 DAO 时遇到了完全相同的问题。配置是 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})
于 2012-04-25T08:56:37.857 回答
2

做快点:

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

问题解决得又快又好:)

以下是有关http://christopherlakey.com/articles/import-sql.html的一些信息import.sql

于 2013-11-11T17:31:16.080 回答
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);
    }
}
于 2015-03-17T15:30:58.180 回答
0

我会考虑花一些时间来创建几个固定装置,可能使用 DBUnit,您插入 @Before。

顺便说一句,AbstractTransactionalJUnit4Test 在 Spring 3.0 中已弃用

于 2010-04-21T20:44:28.510 回答