Прежде всего: спасибо за эту потрясающую библиотеку! Мы медленно переходим с Hibernate на jdbi и пока очень им довольны;)
Предположим, у меня есть следующий код:
getHandle().createQuery("SELECT ...")
.bind("id", id)
.registerRowMapper((RowMapper<CatalogueJdbi>) (rs, ctx) -> {
final CatalogueJdbi catalogue = FieldMapper.of(CatalogueJdbi.class, "c").map(rs, ctx);
if (rs.getObject("mid") != null) {
catalogue.setImage(FieldMapper.of(MediaFileJdbi.class, "m").map(rs, ctx));
}
return catalogue;
})
.mapTo(CatalogueJdbi.class)
.collect(Collectors.toSet());
При регистрации RowMapper, как показано, я получаю
java.lang.UnsupportedOperationException: Must use a concretely typed RowMapper here
at org.jdbi.v3.core.mapper.InferredRowMapperFactory.lambda$new$0(InferredRowMapperFactory.java:38)
at java.base/java.util.Optional.orElseThrow(Optional.java:385)
at org.jdbi.v3.core.mapper.InferredRowMapperFactory.<init>(InferredRowMapperFactory.java:38)
at org.jdbi.v3.core.mapper.RowMappers.register(RowMappers.java:63)
at org.jdbi.v3.core.config.Configurable.lambda$registerRowMapper$16(Configurable.java:248)
at org.jdbi.v3.core.config.Configurable.configure(Configurable.java:74)
at org.jdbi.v3.core.config.Configurable.registerRowMapper(Configurable.java:248)
at ...
но при использовании
.registerRowMapper(new RowMapper<CatalogueJdbi>() {
<strong i="13">@Override</strong>
public CatalogueJdbi map(final ResultSet rs, final StatementContext ctx) throws SQLException {
final CatalogueJdbi catalogue = FieldMapper.of(CatalogueJdbi.class, "c").map(rs, ctx);
if (rs.getObject("mid") != null) {
catalogue.setImage(FieldMapper.of(MediaFileJdbi.class, "m").map(rs, ctx));
}
return catalogue;
}
})
вместо этого все работает нормально.
Я думал, лямбды - это всего лишь синтаксический сахар, и компилятор все равно расширит код?
Из-за общего стирания параметр <CatalogueJdbi>
теряется во время выполнения, поэтому Jdbi не знает, какой тип соответствует этому сопоставителю.
Вы можете использовать альтернативный метод, в котором вы передаете тип как отдельный параметр:
.registerRowMapper(CatalogueJdbi.class, (rs, ctx) -> { ... })
Следует отметить одно: регистрация преобразователя строк с последующим вызовом mapTo(type)
- это напрасная трата усилий, если этот преобразователь больше нигде не используется. Вы можете просто вызвать метод map(RowMapper<T>)
и передать ему лямбду:
.map((rs, ctx) -> { ... })
Я знал, что делаю что-то не так :) Большое спасибо!
Как продолжение: когда вы создаете анонимный внутренний класс, вы создаете конкретно типизированный картограф.
Под конкретно типизированным мы подразумеваем то, что Jdbi может получить класс сопоставителя и рефлексивно обнаружить общий параметр T
для RowMapper<T>
. Делаете ли вы это через полноценный класс, например class FooMapper implements RowMapper<Foo> { ... }
или через анонимный внутренний класс, например, new RowMapper<Foo>() { ... }
, для Jdbi это выглядит одинаково.
Самый полезный комментарий
Из-за общего стирания параметр
<CatalogueJdbi>
теряется во время выполнения, поэтому Jdbi не знает, какой тип соответствует этому сопоставителю.Вы можете использовать альтернативный метод, в котором вы передаете тип как отдельный параметр:
Следует отметить одно: регистрация преобразователя строк с последующим вызовом
mapTo(type)
- это напрасная трата усилий, если этот преобразователь больше нигде не используется. Вы можете просто вызвать методmap(RowMapper<T>)
и передать ему лямбду: