A veces puede ponerse peludo cuando las funciones tienen una firma como esta:
fun doStuff(firstKey: UUID, secondKey: UUID, ...)Para el compilador, todos los UUID son iguales, por lo que es de esperar que la base de datos detecte un problema en tiempo de ejecución.
Me gusta cómo jOOQ detecta muchos problemas en tiempo de compilación, y me gustaría abordar este también. Mi objetivo es tener para cada clave de cada tabla su propia clase, y tener los pojos generados correctamente con estos campos también.
¿Cuál sería la mejor manera de lograrlo? Se me ha ocurrido lo siguiente:
JavaGeneratorConverters con muchas asignaciones de tipos forzados y clases de claves creadas manualmente¿Alguien tiene experiencia con algo así?
No eres el primero con esta idea. Hay una solicitud de función pendiente para generar tales clases de "envoltura de clave" listas para usar (aún no disponible en 3.11): https://github.com/jOOQ/jOOQ/issues/6124
O esta discusión: https://groups.google.com/forum/#!topic/jooq-user/53RZqoewa3g
Hay aplicaciones adicionales a la suya para esta característica. Una vez que tales tipos existen:
El caso de la clave compuesta es el que hace que jOOQ sea difícil de admitir desde el primer momento, ya que primero se debe implementar una variedad de características adicionales para agrupar varias columnas en una columna sintética. Además, tanto las claves únicas como las claves externas pueden superponerse, por lo que hay bastantes casos extremos que deben tenerse en cuenta.
Puede implementar esto usted mismo. Si su esquema solo tiene claves sustitutas de una sola columna, entonces podría anular la clase JavaGenerator y generar una clase adicional por tabla y agregar las configuraciones de forcedType relevantes y las implementaciones de Converter mediante programación a su configuración del generador de código.
Es posible que otros hayan hecho algo como esto, pero no conozco ninguna implementación disponible públicamente.
En principio, también podría implementar esto directamente en la base de datos, por ejemplo, si está utilizando PostgreSQL. Puede envolver su UUID en un tipo compuesto y usarlo para sus claves principales/claves externas:
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);El generador de código jOOQ debería recoger estos tipos y hacer lo correcto por usted. Por supuesto, probablemente haya algunas advertencias, dado que casi nunca he visto esta práctica en la naturaleza :-)
¿Alguien tiene experiencia con algo así?
Sí, lo he hecho para proyectos Java y Kotlin. Todas las tablas usan claves sustitutas basadas en UUID y siguen estas convenciones de nomenclatura:
ADDRESS.IDORDER.ADDRESS_ID o ORDER.SHIPPING_ADDRESS_IDPara el proyecto Kotlin, usé
Esto tarda aproximadamente 2 días en configurarse correctamente y nos dio un gran impulso en la seguridad de tipos y la claridad del código. En mi humilde opinión, vale la pena el esfuerzo para un proyecto estimado> 1 persona-año.
(De hecho, fui un paso más allá y escribí una pequeña herramienta de "andamiaje de código" que generaría los tres artefactos anteriores. Esto me evita tener que jugar con archivos en diferentes lugares, lo que puede ser propenso a errores pero en su mayoría molesto. Esto me tomó otro día para escribir, aunque esto depende en gran medida de la configuración de su proyecto).
La razón por la que elegimos no resolver esto a través de una implementación personalizada de JooqGenerator es que también usamos las mismas clases XXXId en el código de dominio, que no debe depender de los artefactos de la capa de datos por razones arquitectónicas. Usamos un enfoque casi idéntico para las enumeraciones, donde ha resultado especialmente útil usar el tipo de dominio en el modelo JOOQ en lugar de viceversa. Por ejemplo, es trivial documentar un valor de enumeración definido manualmente en el código, algo que extrañaría en una clase generada. También pudimos definir de manera trivial las relaciones de subtipo entre ContactId y CustomerId , algo que sería muy engorroso con las clases generadas.
Puedo proporcionar fragmentos para darle una mejor idea, pero desafortunadamente no toda la implementación, ya que es un código propietario que no me pertenece.