类型推断似乎失败了 vavr 的 Try 对 jOOQ 的 fetchOne() 函数有效
Type inference seems to fail vavr's Try works on jOOQ's fetchOne() function
我正在使用 vavr 和 jOOQ,这是最近出现的两个很棒的库,它们允许我们在常规 Java 服务器应用程序中使用函数式方言。
我正在尝试使用 jOOQ,相当于 SQL 的 select count (*).
查询是这样构成的:
ResultQuery query = dsl.selectCount()
.from(Tables.SH_PLAYER_REPORT)
.join(Tables.SH_PLAYERS)
.on(Tables.SH_PLAYERS.PLAYER_ID.eq(Tables.SH_PLAYER_REPORT.PLAYER_ID))
.join(Tables.SH_LOCATION)
.on(Tables.SH_LOCATION.LOCATION_ID.eq(Tables.SH_PLAYERS.LOCATION))
.and(Tables.SH_PLAYER_REPORT.START_ON.ge(Timestamp.from(fromWhenInUTCInstant)))
.and(Tables.SH_PLAYER_REPORT.START_ON.le(Timestamp.from(toWhenInUTCInstant)))
.and(Tables.SH_LOCATION.LOCATION_ID.eq(criteriaAllFieldsTeam.getLocation_id()));
jOOQ 的生成器工作完美,这里没有任何类型不匹配。所以,我想查询的格式是正确的。
然后,我使用vavr的Try,因此:
Optional<Integer> mayBeCount = Optional.empty();
try (final Connection cn = this.ds.getConnection()) {
DSLContext dsl = DSL.using(cn, this.dialect);
Try<Integer> countFromDBAttempted =
Try
.of(() -> prepareCountOfGamesPlayedQuery(dsl,criteriaAllFieldsTeam))
.map(e -> e.fetchOne(0, Integer.class)) // Here's the problem!
.onFailure(e -> logger.warning(String.format("Count Of Games Played, status=Failed, reason={%s}",e.getMessage())));
mayBeCount = (countFromDBAttempted.isFailure() ? Optional.empty() : Optional.of(countFromDBAttempted.getOrElse(0)));
} catch (SQLException ex) {
logger.warning(
String.format("DB(jOOQ): Failed, counting games played, using criteria {%s},reason={%s}",criteriaAllFieldsTeam.toString(),ex.getMessage()));
}
return (mayBeCount);
编译器无法推断字段的类型,尽管我通过描述目标类型为它提供了帮助:Integer.class!
../ReportByTeamRecordProducerImpl.java:66: error: incompatible types: Try<Object> cannot be converted to Try<Integer>
.onFailure(e -> logger.warning(String.format("Count Of Games Played, status=Failed, reason={%s}",e.getMessage())));
^
毫不奇怪,当我强制转换类型时,代码运行得非常好。我只是在编译器认为 .. 呃 .. 令人反感的行中引入了一个显式转换!
Try<Integer> countFromDBAttempted =
Try
// The following function returns the ResultQuery shown above
.of(() -> prepareCountOfGamesPlayedQuery(dsl,criteriaAllFieldsTeam))
// Casting below, because of some incompatibility between vavr and jOOQ
.map(e -> ((Integer) e.fetchOne(0, Integer.class)))
.onFailure(e -> logger.warning(String.format("Count Of Games Played, status=Failed, reason={%s}",e.getMessage())));
根据我对 jOOQ 库,尤其是这个 explanation by @LukasEder 的理解,我尝试了其他几种方法。
到目前为止我还没有尝试过的是引入一个Converter,因为对于单个字段值,这在我看来似乎是不必要的!但是,如果是这样的话,那么我想得到一个提示。
回应@LukasEder :
private ResultQuery prepareCountOfGamesPlayedQuery(DSLContext dsl, CriteriaAllFieldsTeam criteriaAllFieldsTeam) {
Instant fromWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getFromWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
Instant toWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getToWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
ResultQuery query = dsl.selectCount()
.from(Tables.SH_PLAYER_REPORT)
.join(Tables.SH_PLAYERS)
.on(Tables.SH_PLAYERS.PLAYER_ID.eq(Tables.SH_PLAYER_REPORT.PLAYER_ID))
.join(Tables.SH_LOCATION)
.on(Tables.SH_LOCATION.LOCATION_ID.eq(Tables.SH_PLAYERS.LOCATION))
.and(Tables.SH_PLAYER_REPORT.START_ON.ge(Timestamp.from(fromWhenInUTCInstant)))
.and(Tables.SH_PLAYER_REPORT.START_ON.le(Timestamp.from(toWhenInUTCInstant)))
.and(Tables.SH_LOCATION.LOCATION_ID.eq(criteriaAllFieldsTeam.getLocation_id()));
return (query);
}
在Lukas的推动下,我将方法修改为:
private ResultQuery<Record1<Integer>> prepareCountOfGamesPlayedQuery(DSLContext dsl, CriteriaAllFieldsTeam criteriaAllFieldsTeam) {
Instant fromWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getFromWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
Instant toWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getToWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
ResultQuery<Record1<Integer>> query = dsl.selectCount()
.from(Tables.SH_PLAYER_REPORT)
.join(Tables.SH_PLAYERS)
.on(Tables.SH_PLAYERS.PLAYER_ID.eq(Tables.SH_PLAYER_REPORT.PLAYER_ID))
.join(Tables.SH_LOCATION)
.on(Tables.SH_LOCATION.LOCATION_ID.eq(Tables.SH_PLAYERS.LOCATION))
.and(Tables.SH_PLAYER_REPORT.START_ON.ge(Timestamp.from(fromWhenInUTCInstant)))
.and(Tables.SH_PLAYER_REPORT.START_ON.le(Timestamp.from(toWhenInUTCInstant)))
.and(Tables.SH_LOCATION.LOCATION_ID.eq(criteriaAllFieldsTeam.getLocation_id()));
return (query);
}
..而且,世界又和平了!
谢谢,卢卡斯!
根据您目前提供的代码,假设没有错字,这可能是由于您对 ResultQuery
的原始类型引用造成的。请改用 ResultQuery<?>
或 ResultQuery<Record1<Integer>>
。
永远不要使用原始类型,除非你真的必须这样做。而你可能不知道。
我正在使用 vavr 和 jOOQ,这是最近出现的两个很棒的库,它们允许我们在常规 Java 服务器应用程序中使用函数式方言。
我正在尝试使用 jOOQ,相当于 SQL 的 select count (*).
查询是这样构成的:
ResultQuery query = dsl.selectCount()
.from(Tables.SH_PLAYER_REPORT)
.join(Tables.SH_PLAYERS)
.on(Tables.SH_PLAYERS.PLAYER_ID.eq(Tables.SH_PLAYER_REPORT.PLAYER_ID))
.join(Tables.SH_LOCATION)
.on(Tables.SH_LOCATION.LOCATION_ID.eq(Tables.SH_PLAYERS.LOCATION))
.and(Tables.SH_PLAYER_REPORT.START_ON.ge(Timestamp.from(fromWhenInUTCInstant)))
.and(Tables.SH_PLAYER_REPORT.START_ON.le(Timestamp.from(toWhenInUTCInstant)))
.and(Tables.SH_LOCATION.LOCATION_ID.eq(criteriaAllFieldsTeam.getLocation_id()));
jOOQ 的生成器工作完美,这里没有任何类型不匹配。所以,我想查询的格式是正确的。
然后,我使用vavr的Try,因此:
Optional<Integer> mayBeCount = Optional.empty();
try (final Connection cn = this.ds.getConnection()) {
DSLContext dsl = DSL.using(cn, this.dialect);
Try<Integer> countFromDBAttempted =
Try
.of(() -> prepareCountOfGamesPlayedQuery(dsl,criteriaAllFieldsTeam))
.map(e -> e.fetchOne(0, Integer.class)) // Here's the problem!
.onFailure(e -> logger.warning(String.format("Count Of Games Played, status=Failed, reason={%s}",e.getMessage())));
mayBeCount = (countFromDBAttempted.isFailure() ? Optional.empty() : Optional.of(countFromDBAttempted.getOrElse(0)));
} catch (SQLException ex) {
logger.warning(
String.format("DB(jOOQ): Failed, counting games played, using criteria {%s},reason={%s}",criteriaAllFieldsTeam.toString(),ex.getMessage()));
}
return (mayBeCount);
编译器无法推断字段的类型,尽管我通过描述目标类型为它提供了帮助:Integer.class!
../ReportByTeamRecordProducerImpl.java:66: error: incompatible types: Try<Object> cannot be converted to Try<Integer>
.onFailure(e -> logger.warning(String.format("Count Of Games Played, status=Failed, reason={%s}",e.getMessage())));
^
毫不奇怪,当我强制转换类型时,代码运行得非常好。我只是在编译器认为 .. 呃 .. 令人反感的行中引入了一个显式转换!
Try<Integer> countFromDBAttempted =
Try
// The following function returns the ResultQuery shown above
.of(() -> prepareCountOfGamesPlayedQuery(dsl,criteriaAllFieldsTeam))
// Casting below, because of some incompatibility between vavr and jOOQ
.map(e -> ((Integer) e.fetchOne(0, Integer.class)))
.onFailure(e -> logger.warning(String.format("Count Of Games Played, status=Failed, reason={%s}",e.getMessage())));
根据我对 jOOQ 库,尤其是这个 explanation by @LukasEder 的理解,我尝试了其他几种方法。
到目前为止我还没有尝试过的是引入一个Converter,因为对于单个字段值,这在我看来似乎是不必要的!但是,如果是这样的话,那么我想得到一个提示。
回应@LukasEder :
private ResultQuery prepareCountOfGamesPlayedQuery(DSLContext dsl, CriteriaAllFieldsTeam criteriaAllFieldsTeam) {
Instant fromWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getFromWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
Instant toWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getToWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
ResultQuery query = dsl.selectCount()
.from(Tables.SH_PLAYER_REPORT)
.join(Tables.SH_PLAYERS)
.on(Tables.SH_PLAYERS.PLAYER_ID.eq(Tables.SH_PLAYER_REPORT.PLAYER_ID))
.join(Tables.SH_LOCATION)
.on(Tables.SH_LOCATION.LOCATION_ID.eq(Tables.SH_PLAYERS.LOCATION))
.and(Tables.SH_PLAYER_REPORT.START_ON.ge(Timestamp.from(fromWhenInUTCInstant)))
.and(Tables.SH_PLAYER_REPORT.START_ON.le(Timestamp.from(toWhenInUTCInstant)))
.and(Tables.SH_LOCATION.LOCATION_ID.eq(criteriaAllFieldsTeam.getLocation_id()));
return (query);
}
在Lukas的推动下,我将方法修改为:
private ResultQuery<Record1<Integer>> prepareCountOfGamesPlayedQuery(DSLContext dsl, CriteriaAllFieldsTeam criteriaAllFieldsTeam) {
Instant fromWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getFromWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
Instant toWhenInUTCInstant =
convertToDBCompatibleInstantUTC(
criteriaAllFieldsTeam.getDate_range().getToWhen(),
criteriaAllFieldsTeam.getDate_range().getInTimeZone());
ResultQuery<Record1<Integer>> query = dsl.selectCount()
.from(Tables.SH_PLAYER_REPORT)
.join(Tables.SH_PLAYERS)
.on(Tables.SH_PLAYERS.PLAYER_ID.eq(Tables.SH_PLAYER_REPORT.PLAYER_ID))
.join(Tables.SH_LOCATION)
.on(Tables.SH_LOCATION.LOCATION_ID.eq(Tables.SH_PLAYERS.LOCATION))
.and(Tables.SH_PLAYER_REPORT.START_ON.ge(Timestamp.from(fromWhenInUTCInstant)))
.and(Tables.SH_PLAYER_REPORT.START_ON.le(Timestamp.from(toWhenInUTCInstant)))
.and(Tables.SH_LOCATION.LOCATION_ID.eq(criteriaAllFieldsTeam.getLocation_id()));
return (query);
}
..而且,世界又和平了!
谢谢,卢卡斯!
根据您目前提供的代码,假设没有错字,这可能是由于您对 ResultQuery
的原始类型引用造成的。请改用 ResultQuery<?>
或 ResultQuery<Record1<Integer>>
。
永远不要使用原始类型,除非你真的必须这样做。而你可能不知道。