带有jooq的kotlin和手动编写表模型而无需代码生成

时间:2018-06-05 18:18:17

标签: sql kotlin jooq

我正在尝试使用jOOQ和Kotlin并看过一些教程和文档,看起来非常好。

但如果jOOQ有一些非常烦人的东西就是代码生成。这似乎太复杂了,最终无法维持。我决定创建自己的表模型(类似于hibernate的工作原理)。

我创建了两个表模型:

用户

data class User(
    val id: String = UUID.randomUUID().toString(),
    val name: String,
    val email: String,
    val password: String? = null
) {
    companion object {
        val TABLE: Table<Record> = DSL.table("user")
        val ID: Field<String> = DSL.field("id", String::class.java)
        val USER_NAME: Field<String> = DSL.field("user_name", String::class.java)
        val EMAIL: Field<String> = DSL.field("email", String::class.java)
        val PASSWORD: Field<String> = DSL.field("password", String::class.java)
    }
}

关注

data class Followers(
    val id: String,
    val followerId: String,
    val userId: String
) {
    companion object {
        val TABLE: Table<Record> = DSL.table("followers")
        val ID: Field<String> = DSL.field("id", String::class.java)
        val FOLLOWER_ID: Field<String> = DSL.field("follower_id", String::class.java)
        val USER_ID: Field<String> = DSL.field("user_id", String::class.java)
    }
}

当我做了一些简单的SQL语句并且它运行得很好时,但是当我尝试下一个语句时,我会遇到异常。

return dsl.select().from(u.TABLE)
            .rightJoin(f.TABLE).on(u.ID.eq(f.FOLLOWER_ID))
            .where(u.ID.eq(id)).fetch().into(User::class.java)

此代码的预期声明是:

select *
from user u
right outer join followers f
on u.id = f.follower_id
where u.id = 'e30919bf-5f76-11e8-8c96-701ce7e27f83';

但是我从这段代码中得到的陈述是:

select *
from user
  right outer join followers
  on id = follower_id
where id = 'e30919bf-5f76-11e8-8c96-701ce7e27f83'

当然,这给了我(理所当然)在where子句中的错误列'id'是不明确的

提出了几个问题:

  1. 在没有代码生成的情况下,是否有更好的方法来声明表模型。
  2. 为什么DSL select没有转换为正确的SQL语句?我做错了什么?

1 个答案:

答案 0 :(得分:15)

首先,关于您不愿意使用代码生成的一些建议:

  我似乎太复杂了,最终无法维持。   所以,我决定创建自己的表模型(类似于hibernate的工作方式)。

你(可能)正在经历痛苦和痛苦的漫长道路。首先,您现在需要考虑数据库迁移,最好使用数据库的DDL语言。这意味着,从长远来看,您的数据数据库模型应该比您的客户端模型更重要。实际上,您的客户端模型是数据库模型的副本,而不是您想要独立维护的内容。有了这种心态,让代码生成器从数据库模型生成客户端模型更合理,反之亦然。

当然,当你启动一个项目时,Hibernate也会使客户端变得容易。然而,一旦你开始生产,你必须迁移你的数据库,然后这个模型将破裂。你首先回到数据库,现在已经开始设置所有内容了。

所以,不。代码生成可能会引入一些复杂性现在,但与您创建自己的表模型相比,将更容易维护

I've written up a longer blog post about this topic, here

关于您的具体问题:

return dsl.select().from(u.TABLE)
          .rightJoin(f.TABLE).on(u.ID.eq(f.FOLLOWER_ID))
          .where(u.ID.eq(id)).fetch().into(User::class.java)
     

此代码的预期声明是:[...]

嗯,这取决于uf是什么。你不能只是将你的Kotlin引用重命名为你的表,并期望jOOQ知道它们的含义。即你可能创建了如下引用:

val u = User.TABLE;
val f = Follower.TABLE;

如果这就是你创建引用的方式,那么这两件事就是按照身份相同的事情。 jOOQ并没有神奇地对你的Kotlin代码进行反向工程,以发现你想要别名你的表。你必须告诉jOOQ:

val u = User.TABLE.as("u");
val f = Follower.TABLE.as("f");

但现在你还没完成。您使用plain SQL API构造了User.TABLE引用,这意味着jOOQ的运行时不知道该表中的列。您不能再从别名表引用这些列,因为纯SQL表的别名表的类型是Table<?>,而不是User

当然,您可以创建TableImpl个实例并在TableImpl实例中注册所有列 - 就像代码生成器一样。在这种情况下,您将拥有与它们关联的表列,并且即使使用别名表也可以使用它们安全地键入。

所有这些东西都是由生成的代码自动处理的,我建议你再次使用jOOQ。任何人都不会将代码生成器与jOOQ一起使用的主要原因是因为数据模型是动态的,即在编译时不知道。否则,您只需要自动重复代码生成器已为您完成的大量工作。而且,如前所述,当您开始迁移架构时,您将在以后做更多工作。