kotlin使用jooq并手动编写表模型而无需生成代码

Aha*_*-El 7 sql jooq kotlin

我正在尝试使用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

关注

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

当我做了一些简单的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)
Run Code Online (Sandbox Code Playgroud)

此代码的预期声明是:

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

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

select *
from user
  right outer join followers
  on id = follower_id
where id = 'e30919bf-5f76-11e8-8c96-701ce7e27f83'
Run Code Online (Sandbox Code Playgroud)

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

它提出了几个问题:

  1. 是否有更好的方法来声明表模型而不生成代码.
  2. 为什么DSL select不能转换为正确的SQL语句?我做错了什么?

Luk*_*der 15

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

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

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

当然,当你启动一个项目时,Hibernate也会使客户端的第一个方法变得容易.然而,一旦你去生产,你不得不迁移你的数据库,然后这个模型将破裂.你首先回到数据库,现在已经值得设置.

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

我在这里写了一篇关于这个主题的博文.

关于您的具体问题:

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)
Run Code Online (Sandbox Code Playgroud)

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

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

val u = User.TABLE;
val f = Follower.TABLE;
Run Code Online (Sandbox Code Playgroud)

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

val u = User.TABLE.as("u");
val f = Follower.TABLE.as("f");
Run Code Online (Sandbox Code Playgroud)

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

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

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