Gei*_*eir 5 java lambda java-8
我正在玩lambdas并进入我的脑海,我想尝试创建一个简单的数据库/对象映射器作为学习的一部分.
是的,有很多框架已经做到了这一点,但这更多的是关于学习和我遇到的问题是技术问题.
首先,我想在枚举中定义所有映射逻辑.
它只用一堆字段名称开始简单明了:
enum ThingColumn {
id, language;
}
Run Code Online (Sandbox Code Playgroud)
这让我创建了以下方法(实现不相关),它给出了api用户对列的编译检查:
public Collection<Thing> findAll(ThingColumn... columns);
Run Code Online (Sandbox Code Playgroud)
之后我想在枚举中定义更多规则,特别是如何将结果从a映射java.sql.ResultSet到我的Thing类.
从简单的开始我创建了一个功能界面:
@FunctionalInterface
static interface ThingResultMapper {
void map(Thing to, ResultSet from, String column) ;
}
Run Code Online (Sandbox Code Playgroud)
并将其添加到枚举中:
enum ThingColumn {
id((t, rs, col) -> t.setId(rs.getLong(col))),
language((t, rs, col) ->t.setLanguage(rs.getString(col)));
ThingColumn(ThingResultMapper mapper){..}
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个mapResultSetRow方法,它使用枚举中的lambda来从以下位置提取数据ResultSet:
public Thing mapResultSetRow(ResultSet rs, ThingColumn... fields) {
Thing t = new Thing();
Stream.of(fields)
.forEach(f -> f.getMapper().map(t, rs, f.name()));
return t;
}
Run Code Online (Sandbox Code Playgroud)
以上findAll可以使用mapResultSetRow相关的映射器来应用ResultSet.干净整洁.
几乎无论如何.我认为枚举非常难看,并且包含很多锅炉板,你必须为每个映射添加lambda.理想情况下,我想这样做:
enum ThingColumn {
id(ResultSet::getLong, Thing::setId),
language(ResultSet::getString, Thing::setLanguage);
}
Run Code Online (Sandbox Code Playgroud)
然而,这当然不能编译,现在我卡住了,非静态/静态的问题..我将首先通过消除一些噪声来分解它:
enum ThingColumn {
id(ResultSet::getLong); // <<- compile error
ThingColumn(Function<String,?> resultSetExtractor) {..}
}
Run Code Online (Sandbox Code Playgroud)
编译错误:Cannot make a static reference to the non-static method getLong(String) from the type ResultSet.
我想我想要的是要么不可能做,要么通过改变枚举构造函数中labmda的签名.
我在这个问题中发现了一个类似的问题: Java 8 中静态方法引用的限制,其中Dmitry Ginzburg的答案(向下滚动,不被接受为正确答案)概述了一些问题,但没有解决方案.
谢谢你到目前为止阅读:)
有什么想法吗?
第一个示例不起作用,因为您需要处理已检查SQLException.这可以很容易地修复.首先,在您的功能界面上声明此异常:
@FunctionalInterface
static interface ThingResultMapper {
void map(Thing to, ResultSet from, String column) throws SQLException;
}
Run Code Online (Sandbox Code Playgroud)
其次,而不是在处理它的枚举中getMapper创建一个map方法:
enum ThingColumn {
id((t, rs, col) -> t.setId(rs.getLong(col))),
language((t, rs, col) ->t.setLanguage(rs.getString(col)));
private ThingResultMapper mapper;
ThingColumn(ThingResultMapper mapper){
this.mapper = mapper;
}
public void map(Thing to, ResultSet from) {
try {
mapper.map(to, from, name());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以毫无问题地使用它:
public Thing mapResultSetRow(ResultSet rs, ThingColumn... fields) {
Thing t = new Thing();
Stream.of(fields).forEach(f -> f.map(t, rs));
return t;
}
Run Code Online (Sandbox Code Playgroud)
与第二个方法的问题是,你有不同的数据类型(Long,String,等).要解决这个问题,您需要一个功能接口来匹配ResultSet::getLong等方法参考:
@FunctionalInterface
static interface ResultGetter<T> {
T get(ResultSet from, String column) throws SQLException;
}
Run Code Online (Sandbox Code Playgroud)
参数ResultSet本身(this对象,类似ResultSet.getLong方法是非静态的)和列.结果类型可能不同,因此它是通用的.
对于Thingsetter,您可以使用标准BiConsumer<Thing, T>类型.您还需要一个通用的参数化构造函数(是的,它们存在!).此构造函数将创建另一个BiConsumer<Thing, ResultSet>可在map方法中使用的类型函数.
这是完整的代码(mapResultSetRow方法与上面相同):
@FunctionalInterface
static interface ResultGetter<T> {
T get(ResultSet from, String column) throws SQLException;
}
enum ThingColumn {
id(ResultSet::getLong, Thing::setId),
language(ResultSet::getString, Thing::setLanguage);
private final BiConsumer<Thing, ResultSet> mapper;
<T> ThingColumn(ResultGetter<T> getter, BiConsumer<Thing, T> setter) {
this.mapper = (t, rs) -> {
try {
setter.accept(t, getter.get(rs, name()));
} catch (SQLException e) {
throw new RuntimeException(e);
}
};
}
public void map(Thing to, ResultSet from) {
this.mapper.accept(to, from);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
414 次 |
| 最近记录: |