CrazyAirhead

疯狂的傻瓜,傻瓜也疯狂——傻方能执著,疯狂才专注!

0%

扩展 easy-query(二)

说明

在使用 easy-query 一段时间之后,发现 Jfinal 的 ActiveRecord 的一个很好用的功能,现在用起来变麻烦了,是什么呢?就是实体的丰富化。与实体的裁剪相对,丰富化就是对实体内容进行扩展,返回给前端,比如,存储的是编码,但多返回名称。为了实现实体的裁剪与丰富化,通过是建立新的 DTO,不同的返回结果,增加不同的 DTO,这就会导致 DTO的膨胀或者爆炸。而 ActiveRecord 和 Modle 内部通过 Map 来承载数据,天然具体扩展能力,不管是裁剪还是丰富化,都不需要额外定义 DTO。在 扩展 easy-query 中,我们提到了实体的裁剪和实现方法,有兴趣的同学可以进一步看一看。

因为 easy-query 是基于Bean 的 getter 和 setter 方法来读取和设置数据的,也就是说虽然编写的 DSL 已经返回了扩展的字段,因为 Model 对象缺失 getter 和 setter 方法,而无法返回新增字段的数据。如果要返回新增字段的数据就是需要给实体增加 getter 和 setter 方法。虽然比起DTO的膨胀好了一点,但变得没那么直观和直接了。

于是在 easy-query 的群里,问了这个问题,作者说是比较麻烦的。前几天是了修改FastBean 和 ModelProxy,仍然是没有办法实现。

img

今天重新查询 easy-query 的文档,发现了 EntityMetadataManager 和作者提供了StreamIterableFactory,通过替换这个两个框架行为最终实现了编写 DSL 能返回需要的数据。暂时还没有处理直接 SQL 查询时的数据丰富化。

实现

简单说明 EntityMetadataManager,其实会解析实体哪些是字段,哪些是属性。StreamIterableFactory 实现的是对结果集的转换处理。在 Model Bean 的需求里,其实希望的是,如果不是字段,1. SQL 多返回的字段也需要被认定为是属性,2. 通过Model的 put 方法设置数据,而不是直接忽略。因此本次的扩展也就是围绕着这两点进行的。

img

ModelEntityMetadataManager

如果是 Model 类型的实体,需要特殊处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Override
public EntityMetadata getEntityMetadata(Class<?> entityClass) {
if (entityClass == null) {
throw new IllegalArgumentException("entityClass");
}
EntityMetadata cacheItem = entityMetadataCache.get(entityClass);
if (cacheItem != null) {
return cacheItem;
}

if (Map.class.isAssignableFrom(entityClass)) {
return entityMetadataCache.computeIfAbsent(
entityClass,
key -> new MapEntityMetadata(Map.class, mapColumnNameChecker, mapKeyNameConversion));
}

if (Row.class.isAssignableFrom(entityClass) || Objects.equals(SqlKit.class, entityClass)) {
return entityMetadataCache.computeIfAbsent(
entityClass,
key -> new RowEntityMetadata(Row.class, mapColumnNameChecker, mapKeyNameConversion));
}

if (Model.class.isAssignableFrom(entityClass)) {
EntityMetadata entityMetadata =
new ModelEntityMetadata(entityClass, mapColumnNameChecker, mapKeyNameConversion);
entityMetadata.init(serviceProvider);

addMetadata(entityMetadata.getTableName(), entityMetadata);
return entityMetadataCache.computeIfAbsent(entityClass, key -> entityMetadata);
}

EntityMetadata entityMetadata = new EntityMetadata(entityClass);
entityMetadata.init(serviceProvider);

addMetadata(entityMetadata.getTableName(), entityMetadata);
return entityMetadataCache.computeIfAbsent(
entityClass,
key -> {
return entityMetadata;
});
}

ModelEntityMetadata

如果属性不是数据库字段,需要将 put 和 set 返回,当作该属性的getter和setter方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public ColumnMetadata getColumnNotNull(String propertyName) {
ColumnMetadata columnMetadata = getColumnOrNull(propertyName);
if (columnMetadata == null) {
String checkField = mapColumnNameChecker.checkColumnName(propertyName);
ColumnOption columnOption = new ColumnOption(false, this, mapKeyNameConversion.convert(checkField), checkField, checkField);
columnOption.setGetterCaller(obj -> {
if (obj instanceof Model<?>) {
Model<?> model = (Model<?>) obj;
return model.get(checkField);
}
return null;
});

columnOption.setSetterCaller((obj, val) -> {
if (obj instanceof Model<?>) {
Model<?> model = (Model<?>) obj;
model.put(checkField, val);
}
});

return new ColumnMetadata(columnOption);
}

return columnMetadata;
}

ModelStreamIterableFactory

需要使用自定义的 ModelStreamIterable

1
2
3
4
5
6
7
public class ModelStreamIterableFactory implements StreamIterableFactory {
@Override
public <T> StreamIterable<T> create(
ExecutorContext context, ResultMetadata<T> resultMetadata, StreamResultSet streamResultSet) {
return new ModelStreamIterable<>(context, resultMetadata, streamResultSet);
}
}

ModelStreamIterable

如果是 Bean 数据,需要使用 ModelBeanStreamIterator。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private Iterator<T> iterator0() throws SQLException {
EntityMetadataTypeEnum entityMetadataType = resultMetadata.getEntityMetadataType();
switch (entityMetadataType) {
case MAP:
{
if (EasyArrayUtil.isNotEmpty(context.getExpressionContext().getResultPropTypes())) {
return EasyObjectUtil.typeCastNullable(
new MapTypeStreamIterator(
context, streamResultSet, EasyObjectUtil.typeCastNullable(resultMetadata)));
}
return new MapStreamIterator<>(context, streamResultSet, resultMetadata);
}
case BASIC_TYPE:
return new BasicStreamIterator<>(context, streamResultSet, resultMetadata);
// case DRAFT:
// return EasyObjectUtil.typeCastNullable(new DraftStreamIterator(context,
// streamResultSet, EasyObjectUtil.typeCastNullable(resultMetadata)));
default:
{
if (resultMetadata.getDataReader() != null) {
return new FastBeanStreamIterator<>(context, streamResultSet, resultMetadata);
}
if (DraftResult.class.isAssignableFrom(resultMetadata.getResultClass())) {
return EasyObjectUtil.typeCastNullable(
new DraftStreamIterator(
context, streamResultSet, EasyObjectUtil.typeCastNullable(resultMetadata)));
}
return new ModelBeanStreamIterator<>(context, streamResultSet, resultMetadata);
}
}
}

ModelBeanStreamIterator

完全重写 getMapColumnMetadata,如果不是字段的情况,需要使用 get 和 put 方法,另外需要设置 JdbcTypeHandler 为 ObjectTypeHandler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private ResultColumnMetadata getMapColumnMetadata(
int index, String columnName, boolean mapToBeanStrict) {
EntityMetadata entityMetadata = resultMetadata.getEntityMetadata();
ColumnMetadata columnMetadata = entityMetadata.getColumnMetadataOrNull(columnName);
if (columnMetadata != null) {
return new EntityResultColumnMetadata(index, entityMetadata, columnMetadata);
}

if (!mapToBeanStrict) {
return resultMetadata.getResultColumnOrNullByPropertyName(index, columnName);
}

ColumnOption columnOption =
new ColumnOption(false, entityMetadata, columnName, columnName, columnName);
columnOption.setGetterCaller(
obj -> {
if (obj instanceof Model<?>) {
return ((Model<?>) obj).get(columnName);
}
return null;
});

columnOption.setSetterCaller(
(obj, val) -> {
if (obj instanceof Model<?>) {
((Model<?>) obj).put(columnName, val);
}
});

columnOption.setJdbcTypeHandler(new ObjectTypeHandler());

columnMetadata = new ColumnMetadata(columnOption);
return new EntityResultColumnMetadata(index, entityMetadata, columnMetadata);
}

效果

查询

CodeColumn(这个 CodeColumn 类字段有点多,且是自动生成的代码,这里就不放代码了)本身不包含 tableName 属性,是通过 CodeTable 的 name 取别名而来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//  @Deform(keep = {"id", "name"})
public List<CodeColumn> simpleListCodeColumn(CodeColumn codeColumn) {
List<CodeColumn> list =
entityQueryable()
.leftJoin(
CodeTable.class,
(t1, t2) -> {
t1.tableId().eq(t2.id());
})
.select(
CodeColumn.class,
(t1, t2) -> Select.of(t1.FETCHER.allFields(), t2.name().as("tableName")))
.toList();
return list;
}

结果

虽然使用的是 CodeColumn 没有 tableName 属性,但是 tableName 的值成功返回了。Solon 的配置字段过滤 Null 值了。

img

代码

扩展 easy-query 的代码已经签入 https://gitee.com/CrazyAirhead/easy-query,增加了

sql-solon-plugin-ext 模块。代码是从我的其他项目迁移过来的,除用了Gradle 和更高版本的 JDK 和 hutool ,因此虽然通过编译,但是仍有可能报错,目前只建议参考,如果要实际使用,碰到问题可能需要自行修复。

欢迎关注我的其它发布渠道