Reputation: 1964
Sometimes it can get hairy when functions have signature like this:
fun doStuff(firstKey: UUID, secondKey: UUID, ...)
To the compiler all UUIDs are the same, so a problem here is hopefully caught at run time by the database.
I like how jOOQ catches many problems at compile time, and I would like to tackle this one too. My goal is to have for every key of every table its own class, and to have the pojos generated correctly with these fields too.
What would be the best way of achieving that? I've come up with the following:
JavaGenerator
implementationConverters
with lot's of forced type mappings, and manually created key classesDoes anybody have experience with something like that?
Upvotes: 3
Views: 505
Reputation: 9900
Does anybody have experience with something like that?
Yes, I've done it for Java and Kotlin projects. All tables use UUID-based surrogate keys and follow these naming conventions:
ADDRESS.ID
ORDER.ADDRESS_ID
or ORDER.SHIPPING_ADDRESS_ID
For the Kotlin project, I used
This takes about 2 day to set up properly and gave us a tremendous boost in type safety and code clarity. IMHO it is well worth the effort for a project estimated >1 person-year.
(I actually went a step further and wrote a small "code scaffolding" tool that would generate the above three artifacts. This saves me from fiddling with files in different places, which may be error-prone but is mostly annoying. This took me another day to write, although this depends largely on your project setup.)
The reason we chose not to solve this via a custom JooqGenerator
implementation is that we use the same XXXId classes in domain code as well, which must not depend on data-layer artifacts for architectural reasons. We use a near-identical approach for enums, where it has proven especially useful to use the domain type in the JOOQ model instead of vice-versa. For example, it is trivial to document a manually defined enum value in code, something I would miss in a generated class. We also were able to trivially define subtype relationships between a ContactId
and CustomerId
, something that would be very cumbersome with generated classes.
I can provide bits and pieces to give you a better idea, but unfortunately not the entire implementation since that is proprietary code I don't own myself.
Upvotes: 1
Reputation: 220932
You're not the first one with this idea. There's a pending feature request to generate such "key wrapper" classes out of the box (not available in 3.11 yet): https://github.com/jOOQ/jOOQ/issues/6124
Or this discussion: https://groups.google.com/forum/#!topic/jooq-user/53RZqoewa3g
There are additional applications to yours for this feature. Once such types exist:
The composite key case is the one that makes this tricky for jOOQ to support out of the box, as a variety of additional features needs to be implemented first in order to group several columns into a synthetic column. Also, both unique keys and foreign keys can overlap, so there are quite a few edge cases that have to be taken into account.
You can implement this yourself. If your schema only has single column surrogate keys, then you could override the JavaGenerator
class and generate an additional class per table and add the relevant forcedType
configurations and Converter
implementations programmatically to your code generator configuration.
Others may have done something like this, but I'm not aware of any publicly available implementation.
In principle, you could also implement this in the database directly, e.g. if you're using PostgreSQL. You could wrap your UUID in a composite type and use that for your primary keys / foreign keys:
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);
The jOOQ code generator should pick up these types and do the right thing for you. Of course, there are probably quite a few caveats, given that I've hardly ever seen this practice in the wild :-)
Upvotes: 2