java - Apache Derby 中作为 ENUM 替换的用户定义类型

标签 java enums derby

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

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

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

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

为了支持这一点,我创建了类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)

但是将字符串值绑定(bind)到 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 文档,我不知道它是否应该是可能的。

欢迎任何想法!

最佳答案

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

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

然后可以实现绑定(bind),例如绑定(bind)变量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 如何将变量绑定(bind)到 JDBC PreparedStatement,或者如何从 ResultSet 获取变量

避免 MySQL 枚举

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

通过避免 Derby 来简化测试

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

当然,异常(exception)情况是,如果您必须在生产中同时支持 Derby 和 MySQL,在这种情况下,数据类型绑定(bind)又是最佳解决方案。

关于java - Apache Derby 中作为 ENUM 替换的用户定义类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45293986/

相关文章:

java - 如何在 Java 中不使用内置方法 .endsWith() 实现 String.endsWith()

c - 在 C 中传递枚举

java - java中类中的通用getter方法

derby - Sqoop 2重新启动,没有更多工作

java - 如何使用方法将两个数组添加在一起

java - 试图创建一个可点击的按钮但在编译时抛出错误

java sql 插入时间戳 java.sql.SQLSyntaxErrorException

sql - 根据列的不同值查询选择行

java - 如何使用反射从 ArrayList 获取最后一个值?

java - 打印枚举的名称