【问题标题】:HSQLDB primary key violation error in JUnit testsJUnit 测试中的 HSQLDB 主键违规错误
【发布时间】:2012-04-27 14:49:41
【问题描述】:

我们有一个使用 JUnit、OpenEJB、Eclipselink 和 HSQLDB 的测试框架。到目前为止一切正常,测试服务层轻而易举。然而,现在我们在对表进行大规模导入(使用服务层、实体管理器)或例如在服务方法中将实体多次持久化到列表时遇到了问题。

这是奇怪的部分:我们的测试似乎只有在使用 Maven 从命令行在足够快的工作站上运行时才会中断。当我通过 Eclipse IDE 运行测试时,一切都很好,但有时也会随机失败。我们怀疑这可能与测试运行的速度有关,听起来很奇怪。例外很简单,因为它基本上告诉我们我们正在尝试添加一个具有现有 ID 的实体。我们多次检查了我们的测试数据和 hsqldb 数据库。没有我们正在尝试使用的带有 id 的预先存在的行。仍然 hsqldb 在某些时候抛出主键异常。从我们的日志中我们可以看到冲突的 ID 并不总是相同的,它可能是 300015 或 300008。

我们在这里束手无策。是否与 HSQLDB 的事务或其他导致过时数据有关?

我们正在使用 HSQLDB 2.2.8、Eclipselink 2.3.0 和 OpenEJB 4.0.0-beta2。

我们尝试添加实体的关系映射如下:

@OneToMany(mappedBy = "invoice", cascade = CascadeType.PERSIST)
private List<InvoiceBalance> getInvoiceBalanceHistory() {
    if (invoiceBalanceHistory == null) {
        this.invoiceBalanceHistory = new ArrayList<InvoiceBalance>();
    }
    return invoiceBalanceHistory;
}

根异常是:

Caused by: java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: unique constraint or index violation; SYS_PK_10492 table: INVOICEBALANCE
at org.hsqldb.jdbc.Util.sqlException(Unknown Source)
at org.hsqldb.jdbc.Util.sqlException(Unknown Source)
at org.hsqldb.jdbc.JDBCPreparedStatement.fetchResult(Unknown Source)
at org.hsqldb.jdbc.JDBCPreparedStatement.executeUpdate(Unknown Source)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:831)
... 82 more
Caused by: org.hsqldb.HsqlException: integrity constraint violation: unique constraint or   index violation; SYS_PK_10492 table: INVOICEBALANCE
at org.hsqldb.error.Error.error(Unknown Source)
at org.hsqldb.Constraint.getException(Unknown Source)
at org.hsqldb.index.IndexAVLMemory.insert(Unknown Source)
at org.hsqldb.persist.RowStoreAVL.indexRow(Unknown Source)
at org.hsqldb.TransactionManager2PL.addInsertAction(Unknown Source)
at org.hsqldb.Session.addInsertAction(Unknown Source)
at org.hsqldb.Table.insertSingleRow(Unknown Source)
at org.hsqldb.StatementDML.insertSingleRow(Unknown Source)
at org.hsqldb.StatementInsert.getResult(Unknown Source)
at org.hsqldb.StatementDMQL.execute(Unknown Source)
at org.hsqldb.Session.executeCompiledStatement(Unknown Source)
at org.hsqldb.Session.execute(Unknown Source)

编辑:

我将主键生成策略从 GenerationType.AUTO(默认情况下似乎使用 TABLE 策略)更改为 IDENTITY。在此之后,我们的质量持续存在似乎不会失败。我仍然不知道为什么 HSQLDB 与 TABLE 策略“不同步”。我不想仅仅因为我们的测试框架有问题就改变我们的 jpa 实体 :)

【问题讨论】:

  • 您使用的是什么版本的 HSQLDB? JUnit 对数据库施加了沉重的事务回滚负载,您可能会遇到错误或已知的工程权衡,以保持 HSQLDB 的快速和小型化。或者可能是您的数据库设置以及 Eclipselink 如何配置身份表和管理它的组合。发布版本号总是一个好主意。
  • 我会将版本号编辑为原始帖子。我正在使用 HSQLDB 2.2.8 Eclipselink 2.3.0 和 OpenEJB 4.0.0-beta2。此外,实体没有使用 IDENTITY 列,而是策略是 AUTO,我认为它在 HSQLDB 上使用 TABLE 策略。
  • 您对发现问题和解决问题的关心程度如何?从 TABLE(或 AUTO,Eclipselink 的 TABLE)切换到 Sequence 或 IDENTITY 可能会解决这个问题。弄清楚它发生的原因将涉及对事务、隔离和回滚设置以及缓存错误等等进行大量痛苦的挖掘。
  • 这是当前的解决方案。这仅意味着 100 多个实体最初使用 TABLE 策略,而需要此类测试的少数例外现在是 IDENTITY。我希望这部分保持一致,但这听起来确实是一个非常耗时的问题。当实现一直在应用程序服务器上运行时,仅仅为了一些随机测试而更改 ID 列生成策略听起来有点错误......哦,好吧。
  • 如果使用数据文件而不是内存会怎样?例如。像这样的连接字符串 jdbc:hsqldb:file:data/db/db;shutdown=true;hsqldb.write_delay_millis=0 另见:stackoverflow.com/questions/8105618/…

标签: java jakarta-ee junit eclipselink hsqldb


【解决方案1】:

您的 allocationSize 可能会在相对较快的平台上或偶尔定义瓶颈。 IE。 当默认为 GenerationType.AUTO 时,默认表 EclipseLink 会将 ID 缓存到分配的值。然后它将查找生成器以确认其最后分配的值。 如果在下一组 ID 被缓存之前在 allocationSize 的边缘进行查找,那么您可能会遇到竞争条件,其中 eclipse 链接在更新缓存之前两次分配缓存中的最后一个 id 并尝试将两者用于插入并且两个插入都失败并回滚。如果可以的话,你应该检查一下当你的分配缓存应该增加时是否会发生这种情况,但也许这种检查可能会改变行为

【讨论】:

  • 有趣的是,将 allocationSize 从 25 增加到 50 似乎可以解决 JUnit 测试中的问题 - 即使在每个测试创建数百个实体时也是如此。由于测试而做出妥协是很糟糕的。
【解决方案2】:

您很可能在将大量行导入MEMORY 表时内存不足。

您应该增加内存分配或将此特定表定义为CACHED 表。

更新:CACHED 表可以在持久性数据库中使用,而不是在全内存数据库中:

CREATE CACHED TABLE mytable ...

或对于现有表:

SET TABLE mytable TYPE CACHED

更新:

如果这不是由 OOM 引起的,正如更改生成策略所证实的那样,那么生成策略似乎可能不会在某些时候增加生成的主键值。身份策略依赖于数据库来创建生成的值,效果很好。

【讨论】:

  • 可能值得一试。如何将单个表定义为缓存在 HSQLDB 中?
  • 我将主键生成策略从 GenerationType.AUTO(默认情况下似乎使用 TABLE 策略)更改为 IDENTITY。在此之后,我们的质量持续存在似乎不会失败。我仍然不知道为什么 HSQLDB 与 TABLE 策略“不同步”。
【解决方案3】:

对于integrity constraint violation: unique constraint or index violation 如果你是一个调试狂,你可以在调试模式下重新构建 hsqldb,并在 org.hsqldb.index.IndexAVLMemory#insert 中设置一个断点,其中变量 compare 已被分配了断点 compare == 0 上的条件。

错误的行(例如重复的行)将是作为参数传递的行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 2018-11-12
    相关资源
    最近更新 更多