【问题标题】:User-defined types in Apache Derby as ENUM replacementsApache Derby 中的用户定义类型作为 ENUM 替换
【发布时间】:2017-12-30 19:55:36
【问题描述】:

我正在使用 Apache Derby 作为内存中的模拟数据库,用于对使用 jOOQ 的一些与 MySQL 配合使用的代码进行单元测试。

生产数据库对某些字段使用枚举(这是给定的,超出了这个问题的范围 - 我知道枚举不好,但我现在不能更改这部分),所以 jOOQ 生成代码来处理枚举。

不幸的是,Derby 不支持枚举,当我尝试在 Derby 中创建数据库(来自 jOOQ SQL 生成器)时,我得到了错误。

我的解决方案是通过包装相关的 jOOQ 生成的枚举 Java 类来模拟枚举的用户定义类型。因此,例如,如果我在表 stuffs 中有一个枚举字段 kind,jOOQ SQL 生成器将创建 Derby 表创建 SQL,它讨论 stuffs_kind

为了支持这一点,我创建了类 my.project.tests.StuffsKindDebyEnum,它包装了 jOOQ 生成的枚举类型 my.project.model.StuffsKind。然后,在运行 jOOQ 数据库创建 SQL 之前,我通过 Derby 运行以下 SQL:

CREATE TYPE stuffs_kind EXTERNAL NAME 'my.project.tests.StuffsKindDerbyEnum' LANGUAGE JAVA

当我使用 jOOQ 插入新记录时,jOOQ 生成的 SQL 看起来有点像这样:

insert into "schema"."stuffs" ("text", "kind") 
  values (cast (? as varchar(32672)), cast(? as stuffs_kind)

但是将字符串值绑定到 kind 参数(如预期的那样),它适用于 MySQL,但使用 Derby 我得到一个异常:

java.sql.SQLDataException: An attempt was made to get a data value of type
  '"APP"."STUFFS_KIND"' from a data value of type 'VARCHAR'

在查看了解决此问题的各种方法(包括尝试将枚举视为简单的 VARCHAR)之后,并且在我放弃能够测试我的 jOOQ 使用代码之前,有没有办法让 Derby 获得“将“varchar 转换为用户定义的类型?如果可以放置一些可以处理该问题的 Java 代码,那将不是问题,因为我可以简单地执行 StuffsKind.valueOf(value) 将字符串转换为正确的枚举类型,但是在仔细阅读(非常少的)Derby 文档之后,我不能弄清楚它是否应该是可能的。

欢迎提出任何想法!

【问题讨论】:

  • 一旦您开始使用供应商特定的功能,使用“内存模拟数据库”总是很痛苦。使用实际的 MySQL 数据库(例如在 Docker 中)进行集成测试怎么样?
  • 除非我找到其他解决方案,否则我必须这样做,但我真的不想这样做,因为它会引起很多头痛(不能并行运行测试,痛苦设置过程等)。
  • 另一种选择,也许是这个东西:github.com/vorburger/MariaDB4j。我还没有尝试过,但它看起来对我的用例很有趣。
  • 为什么不能在 Docker 和/或 Testcontainers 或类似的设备上并行运行测试?设置过程有什么痛苦?
  • 我已经查看了 Testcontainers,它看起来很有希望。如果我需要 Windows 支持,MariaDB4j 可能是一个更好的选择,但由于情况并非如此,我将尝试使用 Testcontainers 继续前进,但我也会尝试看看您下面的答案是否可以解决我的问题。跨度>

标签: java enums derby


【解决方案1】:

实现方言敏感的自定义数据类型绑定:

正确的方法是使用方言敏感的自定义数据类型绑定: https://www.jooq.org/doc/latest/manual/sql-building/queryparts/custom-bindings

然后绑定可以实现,例如绑定变量SQL生成如下:

@Override
public void sql(BindingSQLContext<StuffsKindDerbyEnum> ctx) throws SQLException {
    if (ctx.family() == MYSQL)
        ctx.render().visit(DSL.val(ctx.convert(converter()).value()));
    else if (ctx.family() == DERBY)
        ctx.render()
           .sql("cast(
           .visit(DSL.val(ctx.convert(converter()).value()))
           .sql(" as varchar(255))");
    else
        throw new UnsupportedOperationException("Dialect not supported: " + ctx.family());
}

您显然还必须实现告诉 jOOQ 如何将变量绑定到 JDBC PreparedStatement 或如何从 ResultSet 获取它的其他方法

避免 MySQL 枚举

另一种更简单的方法可能是避免使用特定于供应商的功能,而只在两个数据库中使用VARCHAR。您仍然可以使用 jOOQ ConverterVARCHAR 映射到 Java enum 类型,这在两个数据库中的工作方式相同。

通过避免 Derby 来简化测试

一个更简单的方法是直接在 MySQL 上测试您的应用程序,例如在内存中的 docker 虚拟化上。数据库供应商及其功能之间存在很多差异,并且在某些时候,为了获得稍微更快的测试而解决这些差异似乎是不合理的。

当然,如果您必须在生产环境中同时支持 Derby 和 MySQL,则例外情况是,在这种情况下,数据类型绑定又是最佳解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-15
    • 2022-07-07
    • 2016-10-23
    • 2013-07-03
    • 2013-11-27
    • 1970-01-01
    • 2021-12-24
    相关资源
    最近更新 更多