自定义数据源 25.02
当您的数据位于应用程序外部 - 在 REST API、数据库或外部服务中 - 您需要创建自定义存储库实现。DelegatingRepository
类通过允许您提供函数而不是实现完整类,使这一过程变得简单明了。
DelegatingRepository
的工作原理
DelegatingRepository
是一个具体类,它扩展了 AbstractQueryableRepository
。您只需在构造函数中提供三个函数,而不是实现抽象方法:
DelegatingRepository<User, UserFilter> repository = new DelegatingRepository<>(
// 1. 查找函数 - 返回过滤/排序/分页数据
criteria -> userService.findUsers(criteria),
// 2. 计数函数 - 返回过滤条件的总数
criteria -> userService.countUsers(criteria),
// 3. 按键查找函数 - 通过 ID 返回单个实体
userId -> userService.findById(userId)
);
每个函数都有特定的目的:
查找函数 接收一个 RepositoryCriteria
对象,其中包含:
getFilter()
- 您的 自定义过滤对象(F
类型参数)getOffset()
和getLimit()
- 用于分页getOrderCriteria()
- 排序规则列表
此函数必须返回与条件匹配的 Stream<T>
实体。如果没有找到匹配项,流可以为空。
计数函数 也接收条件,但通常只使用过滤部分。它返回匹配实体的总数,忽略分页。这被 UI 组件用于显示总结果或计算页面数。
按键查找函数 接收一个实体键(通常是 ID)并返回 Optional<T>
。如果实体不存在,则返回 Optional.empty()
。
REST API 示例
在与 REST API 集成时,您需要将存储库条件转换为 HTTP 请求参数。首先定义一个与您的 API 查询功能匹配的过滤类:
public class UserFilter {
private String department;
private String status;
// 获取器和设置器...
}
这个过滤类表示您的 API 接受的搜索参数。当应用过滤时,存储库将把这个类的实例传递给您的函数。
创建带有将条件转换为 API 调用的函数的存储库:
DelegatingRepository<User, UserFilter> apiRepository = new DelegatingRepository<>(
// 查找用户
criteria -> {
Map<String, String> params = buildParams(criteria);
List<User> users = restClient.get("/users", params);
return users.stream();
},
// 计数用户
criteria -> {
Map<String, String> filterParams = buildFilterParams(criteria.getFilter());
return restClient.getCount("/users/count", filterParams);
},
// 按 ID 查找
userId -> restClient.getById("/users/" + userId)
);
buildParams()
方法将从条件中提取值并将其转换为查询参数,例如 ?department=Sales&status=active&offset=20&limit=10
。您的 REST 客户端随后将实际进行 HTTP 请求并反序列化响应。
数据库示例
数据库集成遵循类似的模式,但将条件转换为 SQL 查询。关键的区别在于 SQL 生成和参数绑定的处理:
DelegatingRepository<Customer, CustomerFilter> dbRepository = new DelegatingRepository<>(
// 带过滤、排序、分页的查询
criteria -> {
String sql = buildQuery(criteria);
return jdbcTemplate.queryForStream(sql, rowMapper);
},
// 计数匹配记录
criteria -> {
String countSql = buildCountQuery(criteria.getFilter());
return jdbcTemplate.queryForObject(countSql, Integer.class);
},
// 按主键查找
customerId -> {
String sql = "SELECT * FROM customers WHERE id = ?";
return jdbcTemplate.queryForObject(sql, rowMapper, customerId);
}
);
buildQuery()
方法将构建类似于:
SELECT * FROM customers
WHERE status = ? AND region = ?
ORDER BY created_date DESC, name ASC
LIMIT ? OFFSET ?
您的过滤对象属性映射到 WHERE
子句条件,而分页和排序则通过 LIMIT/OFFSET
和 ORDER BY
子句处理。
与 UI 组件一起使用
存储库模式的美在于 UI 组件不需要知道或关心数据的来源。无论是内存中的集合、REST API 还是数据库,使用方式都是相同的:
// 创建并配置存储库
Repository<User> repository = createApiRepository();
UserFilter filter = new UserFilter();
filter.setDepartment("Engineering");
repository.setBaseFilter(filter);
// 附加到表格
Table<User> table = new Table<>();
table.setRepository(repository);
// 表格自动显示经过过滤的工程师用户
当用户与 Table
交互(排序列、改变页面)时,Table
会调用您的存储库函数,并带上更新后的条件。您的函数将这些条件转换为 API 调用或 SQL 查询,表格会根据结果自动更新。
何时扩展 AbstractQueryableRepository
如果您需要自定义方法或复杂初始化,请直接扩展 AbstractQueryableRepository
:
public class CustomUserRepository extends AbstractQueryableRepository<User, UserFilter> {
@Override
public Stream<User> findBy(RepositoryCriteria<User, UserFilter> criteria) {
// 实现
}
@Override
public int size(RepositoryCriteria<User, UserFilter> criteria) {
// 实现
}
@Override
public Optional<User> find(Object key) {
// 实现
}
// 添加自定义方法
public List<User> findActiveManagers() {
// 自定义查询逻辑
}
}