8

I'm experimenting with jOOQ and Kotlin and seen some tutorials and docs and it looks really nice.

But if there is something very annoying with jOOQ is the code generation. It seems too complex, and eventually impossible to maintain. I decided to create my own table models (similar to how hibernate works).

I created two table models:

User

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)
    }
}

Followers

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)
    }
}

When I did some trivial SQL statements and it worked perfectly, but when I tried the next statement, I'm getting exception.

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)

The expected statement from this code is:

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

But the statement I got from this code is:

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

And of course, this givse me (rightfully) the error Column 'id' in where clause is ambiguous

It raises a few questions:

  1. Is there a better way to declare table model without code generation.
  2. Why the DSL select does not transform to proper SQL statement? What I'm doing wrong?

1 Answer 1

15

First off, some word of advice on your reluctance to use code generation:

i seems too complex, and eventually impossible to maintain. so, i decided to create my own table models (similar to how hibernate works).

You're (probably) going down a long path of pain and suffering. First off, you will already now need to think of database migrations, which are best done using your database's DDL language. This means, your database model of your data should be more important to you in the long run, than your client model. In fact, your client model is a copy of your database model, not something you'd like to maintain independently. With this mindset, it is more reasonable to have a code generator generate your client model from the database model, not vice versa.

Sure, Hibernate makes the client first approach easy as well, when you start a project. Yet, once you go to production, you will have to migrate your database, and then this model will break. You're back to database first, and it's worth setting up everything already now.

So, no. Code generation might introduce some complexity now, but it will be much more easy to maintain down the road, than you creating your own table models.

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

Regarding your specific questions:

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)

the expected statement from this code is: [...]

Well, that depends on what u and f are. You cannot just rename your Kotlin references to your table and expect jOOQ to know what they mean. I.e. you probably created the references as follows:

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

If that's how you created the reference, then the two things are the same thing by identity. jOOQ doesn't magically reverse engineer your Kotlin code to find out that you meant to alias your table. You have to tell jOOQ:

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

But now you're not done. You constructed the User.TABLE reference using the plain SQL API, which means that jOOQ's runtime has no idea about the columns in that table. You cannot reference those columns anymore from the aliased table, because the type of the aliased table for plain SQL tables is Table<?>, not User.

You could, of course, create TableImpl instances and register all columns inside of your TableImpl instance - just like the code generator does. In that case, you would have tables and columns associated with them, and could use them type safely even with aliased tables.

All of this stuff is handled automatically by generated code, which again, I recommend you use with jOOQ. The main reason why anyone would not use the code generator with jOOQ is because the data model is dynamic, i.e. not known at compile time. Otherwise, you're just going to repeat tons of work that the code generator already does for you, automatically. And, as mentioned before, you will have much more work later on, when you start migrating your schema.

5
  • thank you for your advice and the fully answered question. I can understand better now the power of JOOQ code generation and its importance. Jun 7, 2018 at 23:17
  • @LukasEder what would be a way to solve the problem without code generation? Doing a .as(…) with a qualified name in the on clause doesn't work because it places backticks around the aliased names. Jun 12, 2018 at 4:41
  • @OriPopowski: Can you please ask a new question with more details?
    – Lukas Eder
    Jun 12, 2018 at 8:01
  • @LukasEder sorry to hijack through comment. I saw you are very well familiarized with jooq. I really want to master it through maven plugin usage, flyway execution first all based using spring boot. Can you share/recommend any training course, video or manual to learn it? I find the official documentation kinda annoying to use and navigate. Thanks a lot in advance Sep 2, 2018 at 22:18
  • @FedericoPiazza: Thanks for your message. It's not really related to this question or answer here, though. Feel free to send a message with some details about what you're looking for / what value that would add to your learning / what you find annoying in particular to the jOOQ mailing list: groups.google.com/d/forum/jooq-user
    – Lukas Eder
    Sep 3, 2018 at 7:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.