【问题标题】:kotlin with jooq and write table models manually without code generationkotlin 与 jooq 并手动编写表模型而无需代码生成
【发布时间】:2018-11-15 07:44:03
【问题描述】:

我正在尝试使用 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 语句?我做错了什么?

【问题讨论】:

    标签: sql kotlin jooq


    【解决方案1】:

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

    i 似乎太复杂,最终无法维护。 所以,我决定创建自己的表模型(类似于 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 知道它们的含义。 IE。您可能按如下方式创建了引用:

    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&lt;?&gt;,而不是 User

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

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

    【讨论】:

    • 感谢您的建议和完整回答的问题。我现在可以更好地理解 JOOQ 代码生成的强大功能及其重要性。
    • @LukasEder 在不生成代码的情况下解决问题的方法是什么?在 on 子句中使用限定名称执行 .as(…) 不起作用,因为它会在别名名称周围放置反引号。
    • @OriPopowski:你能问一个更详细的新问题吗?
    • @LukasEder 很抱歉通过评论劫持。我看到你非常熟悉 jooq。我真的很想通过maven插件的使用来掌握它,首先使用spring boot来执行flyway。你能分享/推荐任何培训课程、视频或手册来学习吗?我发现官方文档的使用和导航有点烦人。提前非常感谢
    • @FedericoPiazza:感谢您的留言。不过,这与这里的问题或答案并没有真正的关系。随时向 jOOQ 邮件列表发送一条消息,其中包含有关您正在寻找的内容/可以为您的学习增加什么价值/您发现特别烦人的一些详细信息到 jOOQ 邮件列表:groups.google.com/d/forum/jooq-user
    猜你喜欢
    • 2020-07-26
    • 2018-03-29
    • 2017-03-15
    • 2013-03-01
    • 2014-07-21
    • 2015-12-13
    • 1970-01-01
    • 2014-09-09
    • 2017-12-03
    相关资源
    最近更新 更多