Hi,
I want to check if anyone has a suggestion on using .map() with
.join() in the query.
If you look at the fetchXYZ methods in [1], you'll notice that:
fetchAuthors is fine. It's the simplest case and works as expected.
fetchOneAuthor fails to type check if you use MyTypedMapper. I
initially thought the root cause was the signature of the map method
(it's <E> List<E> map(RecordMapper<? super R, E> var1); in Result.java
but public final <E> E map(RecordMapper<Record, E> mapper) in
AbstractRecord). I however gave up trying to fix this issue as I
clearly don't know type systems.
The maven error message is fantastically useful:
error: method map in class AbstractRecord cannot be applied to given types;
fetchAuthorsOfTitle is the interesting case. It is a common pattern in
the codebase I'm working on. The .join() is not used to fetch more
data, but only to filter data / enforce privacy rules.
Is there a way to avoid having to use an untyped mapper? Can I tell
jOOQ that I want to select on a table and that join should not change
the type of the Record?
fetchOneAuthorWithTransaction is interesting because mvn does not
properly infer the type of Immutable.of() so I need to write
Immutable.<MyAuthor>of(). Intellij doesn't complain. It's not a jOOQ
specific thing, just a quirk which shows up when using closures (which
transactions encourage). I guess it's something that should be filed
as a bug with JetBrains.
Alok
[1] my test code:
package com.squareup.JooqTest;
import com.google.common.collect.ImmutableList;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.RecordMapper;
import org.jooq.impl.DSL;
import test.generated.tables.records.AuthorRecord;
import static test.generated.tables.Author.AUTHOR;
import static test.generated.tables.Book.BOOK;
/**
* create database library;
* use library;
* create table author (id int not null, first_name varchar(255),
last_name varchar(255), primary
* key (id));
* create table book (id int not null, author int not null, title
varchar(255), primary key (id));
* insert into author set id=1, first_name="Isaac", last_name="Asimov";
* ...
* insert into book set id=1, author=1, title="nightfall";
* ...
*/
public class App {
public static void main(String[] args) throws Exception {
App app = new App();
app.run();
}
public void run() throws Exception {
String userName = "root";
String password = "*****";
String url = "jdbc:mysql://localhost:3306/library";
try (Connection conn = DriverManager.getConnection(url, userName,
password)) {
DSLContext jooqContext = DSL.using(conn);
fetchAuthors(jooqContext);
fetchOneAuthor(jooqContext);
fetchAuthorsOfTitle(jooqContext, "nightfall");
fetchOneAuthorWithTransaction(jooqContext);
}
}
List<MyAuthor> fetchAuthors(DSLContext jooqContext) {
return jooqContext.selectFrom(AUTHOR).fetch().map(new MyTypedMapper());
}
MyAuthor fetchOneAuthor(DSLContext jooqContext) {
return jooqContext.selectFrom(AUTHOR).fetchOne().map(new MyUntypedMapper());
}
List<MyAuthor> fetchAuthorsOfTitle(DSLContext jooqContext, String title) {
return jooqContext
.select()
.from(AUTHOR)
.join(BOOK).on(AUTHOR.ID.eq(BOOK.AUTHOR))
.where(BOOK.TITLE.eq(title))
.fetch()
.map(new MyUntypedMapper());
}
List<MyAuthor> fetchOneAuthorWithTransaction(DSLContext jooqContext) {
return jooqContext.transactionResult(configuration -> {
MyAuthor r = fetchOneAuthor(jooqContext);
if (r != null) {
return ImmutableList.of(r);
}
return ImmutableList.<MyAuthor>of();
});
}
class MyTypedMapper implements RecordMapper<AuthorRecord, MyAuthor> {
public MyAuthor map(AuthorRecord record) {
MyAuthor r = new MyAuthor();
r.id = record.getId();
r.firstName = record.getFirstName();
r.lastName = record.getLastName();
return r;
}
}
class MyUntypedMapper implements RecordMapper<Record, MyAuthor> {
public MyAuthor map(Record record) {
MyAuthor r = new MyAuthor();
r.id = record.getValue(
AUTHOR.ID);
r.firstName = record.getValue(AUTHOR.FIRST_NAME);
r.lastName = record.getValue(AUTHOR.LAST_NAME);
return r;
}
}
class MyAuthor {
public int id;
public String firstName;
public String lastName;
}
}