【问题标题】:TDD with HSQLDB -- removing foreign keys使用 HSQLDB 的 TDD——删除​​外键
【发布时间】:2011-02-10 17:35:05
【问题描述】:

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

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

我想要的是:

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

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

你怎么看?这个可以吗?

【问题讨论】:

    标签: hibernate spring tdd foreign-keys hsqldb


    【解决方案1】:

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

    SET REFERENTIAL_INTEGRITY FALSE;
    

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

    【讨论】:

    • 谢谢!!!你只是单枪匹马地把我的测试课减半。有趣的是,这也使得更容易根除 Hibernate 导致不必要的内部连接的地方。
    • 对于任何使用 AbstractTransactionalJUnit4SpringContextTests 的人...神奇之处在于:simpleJdbcTemplate.getJdbcOperations().execute("SET REFERENTIAL_INTEGRITY FALSE;");
    • @roufamatic 很高兴你觉得它有用。
    【解决方案2】:

    我在尝试使用平面 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})
    

    【讨论】:

    • 这是一种优雅的方式。
    • 您不应该在每次测试开始时(在设置数据库之后)使用“SET DATABASE REFERENTIAL INTEGRITY TRUE”将其重新设置吗?
    • 在 setUp 中设置此类代码也可能满足您的需求。就我而言,整个测试套件都依赖于这种结构修改。但结果应该是一样的。
    【解决方案3】:

    快点:

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

    问题解决得又快又好:)

    这里有一些关于import.sqlhttp://christopherlakey.com/articles/import-sql.html的信息

    【讨论】:

      【解决方案4】:

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

      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);
          }
      }
      

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

        • re: deprecated... 我的意思是“AbstractTransactionalJUnit4SpringContextTests”,我在上面更正了。 FWIW 我们使用的是 Spring 2.5。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-09
        相关资源
        最近更新 更多