用jOOQ生成密钥类

时间:2019-01-17 16:17:01

标签: kotlin jooq

有时,当函数具有如下特征时,它可能会变得毛茸茸:

fun doStuff(firstKey: UUID, secondKey: UUID, ...)

对于编译器而言,所有UUID都是相同的,因此希望在运行时数据库能够在此捕获问题。

我喜欢jOOQ在编译时如何解决许多问题,我也想解决这个问题。我的目标是为每个表的每个键都有自己的类,并使用这些字段正确生成pojo。

实现这一目标的最佳方法是什么?我提出了以下建议:

  • 全面实施JavaGenerator的实现
  • Converters,其中包含许多强制类型映射和手动创建的键类

有人有类似的经验吗?

2 个答案:

答案 0 :(得分:1)

路线图上的jOOQ功能

您不是第一个有这个想法的人。有一个未决的功能请求,可以立即生成此类“密钥包装器”类(在3.11中尚不可用): https://github.com/jOOQ/jOOQ/issues/6124

或者这个讨论: https://groups.google.com/forum/#!topic/jooq-user/53RZqoewa3g

此功能还有其他应用程序。一旦此类类型存在:

  • 您只能通过匹配主键/外键进行联接,因为它们将不再由任意数字类型建模
  • 加入或过滤时将不可能忘记组合键中的一列,因为组合键将成为一个值

复合键案例使jOOQ难以立即使用,因为首先需要实现各种附加功能才能将几列分组为一个合成列。另外,唯一键和外键都可以重叠,因此必须考虑很多边缘情况。

自己实施

您可以自己实现。如果您的架构仅具有单列代理键,则可以覆盖JavaGenerator类并为每个表生成一个附加类,然后以编程方式将相关的forcedType配置和Converter实现添加到代码生成器中配置。

其他人可能已经做了类似的事情,但是我不知道任何公开可用的实现。

在数据库中实现

原则上,您也可以直接在数据库中实现此功能,例如如果您使用的是PostgreSQL。您可以将UUID包装为复合类型,并将其用于主键/外键:

create type pk_a as (id bigint);
create type pk_b as (id bigint);

create table t_a(id pk_a primary key);
create table t_b(id pk_b primary key, a pk_a references t_a);

insert into t_a values(row(1)::pk_a);
insert into t_b values(row(2)::pk_b, row(1)::pk_a);

jOOQ代码生成器应该选择这些类型并为您做正确的事情。当然,考虑到我很少在野外看到这种做法,可能有很多警告:-)

答案 1 :(得分:1)

  

有人有类似的经验吗?

是的,我已经在Java和Kotlin项目中做到了。所有表都使用基于UUID的代理键,并遵循以下命名约定:

  • 主键:ADDRESS.ID
  • 外键:ORDER.ADDRESS_IDORDER.SHIPPING_ADDRESS_ID

对于Kotlin项目,我使用了

  • 手动编写的XXXId类,但是定义一个Id-Class及其工厂对象所用的Kotlin不超过4行。
  • 手动编写的XXXJooqIdConverter类,每个类1行。所有的硬部分和实用程序都隐藏在一个通用超类中,该超类从其通用参数中反射性地提取目标类型。
  • 所有forceType映射的静态列表(每行1行,其中包含一个小的辅助函数,仅使用完全限定的Id类名),这仅仅是因为它非常简单。您可以根据需要使用命名约定来生成这些映射。

这大约需要2天的时间才能正确设置,并极大地提高了类型安全性和代码清晰度。恕我直言,为一个估计超过1个人年的项目付出的努力是值得的。

(实际上,我进一步走了一步,编写了一个小的“代码脚手架”工具,该工具会生成上述三个工件。这使我不必在不同位置摆弄文件,这虽然容易出错,但通常很烦人。这我花了一天的时间写,尽管这在很大程度上取决于您的项目设置。)

我们之所以选择不通过自定义JooqGenerator实现来解决此问题,是因为我们也在域代码中使用了相同的XXXId类,出于架构上的原因,这些类不能依赖于数据层构件。我们对枚举使用近似相同的方法,事实证明,在JOOQ模型中使用域类型(而不是相反)特别有用。例如,在代码中记录手动定义的枚举值很简单,这在生成的类中会丢失。我们还可以轻松定义ContactIdCustomerId之间的子类型关系,这对于生成的类来说非常麻烦。

我可以提供一些细节,以使您有一个更好的主意,但不幸的是,不是全部实现,因为那是我不拥有的专有代码。