说明
在使用 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,仍然是没有办法实现。

今天重新查询 easy-query 的文档,发现了 EntityMetadataManager 和作者提供了StreamIterableFactory,通过替换这个两个框架行为最终实现了编写 DSL 能返回需要的数据。暂时还没有处理直接 SQL 查询时的数据丰富化。
实现
简单说明 EntityMetadataManager,其实会解析实体哪些是字段,哪些是属性。StreamIterableFactory 实现的是对结果集的转换处理。在 Model Bean 的需求里,其实希望的是,如果不是字段,1. SQL 多返回的字段也需要被认定为是属性,2. 通过Model的 put 方法设置数据,而不是直接忽略。因此本次的扩展也就是围绕着这两点进行的。

如果是 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; }); }
|
如果属性不是数据库字段,需要将 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); 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
| 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 值了。

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