Piotrowy
Piotrowy

Reputation: 605

jooq cannot convert from class a to class b

I have problem, which I think can be easly solved, but I can't do it.

I have a class A and B and class A is stored in postgres db:

public class A() {
    private B b;
    ...
}

public class B() {
    private String firstName;
    private String lastName;
    private String personId;
    private String userName;
}

In database, object of class b is stored as string userName in A entity. So to have object of class A from db in app, I have to read A record and create object of class B. I want to create query with jooq, but jooq does not know how to convert this string username to instance of class B. How to tell jooq how it should map database A object to object of class A.

A class is equivalent to Person and B class is equivalent to Executor.

My query

return jooq.select()
    .from(PERSON)
    .where(PERSON.ID.eq(id))
    .fetchOneInto(Person.class);

This is the exception thrown by my query

Caused by: org.jooq.exception.DataTypeException: Cannot convert from superadmin (class java.lang.String) to class fi.ssm.oksa.domain.person.Executor
at org.jooq.tools.Convert$ConvertAll.fail(Convert.java:1118) ~[jooq-3.8.5.jar:na]
...

I think I should use this http://www.jooq.org/doc/3.8/manual/code-generation/custom-data-type-bindings/ but I can't implement it.

Upvotes: 4

Views: 2424

Answers (1)

Lukas Eder
Lukas Eder

Reputation: 220762

There are different ways to solve this:

Use jOOQ's undocumented (as of version 3.8) nested record mapping feature

jOOQ has an undocumented feature in its DefaultRecordMapper that allows you to nest result records when you use any of the into(Class) methods. You have to manually specify the "path" of the nested record as if you were actually using ORDBMS nested records, as such:

jooq.select(
       PERSON.FIRST_NAME.as("executor.first_name"),
       PERSON.LAST_NAME.as("executor.last_name"),
       PERSON.PERSON_ID.as("executor.person_id"),
       PERSON.USER_NAME.as("executor.user_name"))
    .from(PERSON)
    .where(PERSON.ID.eq(id))
    .fetchOneInto(Person.class);

When you alias your columns this way, then the DefaultRecordMapper will search for the executor property in the Person class, and put the trailing sub-path into a nested object.

In this example, I'm going to assume the slightly modified versions of your classes:

// instead of A
public class Person {
    private Executor executor;
    ...
}

// instead of B
public class Executor {
    private String firstName;
    private String lastName;
    private String personId;
    private String userName;
}

Override the DefaultRecordMapper

You can implement your own algorithms for mapping when using the into(Class) methods as documented here:

http://www.jooq.org/doc/latest/manual/sql-execution/fetching/pojos-with-recordmapper-provider

Use an explicit record mapper:

Of course, nothing forces you to rely on jOOQ's built-in automatic mapping features. You could write your own algorithm like this, for instance:

jooq.select()
    .from(PERSON)
    .where(PERSON.ID.eq(id))
    .fetchOne(r -> new Person(new Executor(r.get(PERSON.FIRST_NAME), ...));

Upvotes: 2

Related Questions