跳到主要内容
版本:2.20

与自定义模型交互

Halo 提供了两个类用于与自定义模型对象交互 ExtensionClientReactiveExtensionClient

它们提供了对自定义模型对象的增删改查操作,ExtensionClient 是阻塞式的用于后台任务如控制器中操作数据,而 ReactiveExtensionClient 返回值都是 Mono 或 Flux 是反应式非阻塞的,它们由 reactor 提供。

public interface ReactiveExtensionClient {

// 已经过时,建议使用 listBy 或 listAll 代替
<E extends Extension> Flux<E> list(Class<E> type, Predicate<E> predicate,
Comparator<E> comparator);

// 已经过时,建议使用 listBy 或 listAll 代替
<E extends Extension> Mono<ListResult<E>> list(Class<E> type, Predicate<E> predicate,
Comparator<E> comparator, int page, int size);

<E extends Extension> Flux<E> listAll(Class<E> type, ListOptions options, Sort sort);

<E extends Extension> Mono<ListResult<E>> listBy(Class<E> type, ListOptions options,
PageRequest pageable);

/**
* Fetches Extension by its type and name.
*
* @param type is Extension type.
* @param name is Extension name.
* @param <E> is Extension type.
* @return an optional Extension.
*/
<E extends Extension> Mono<E> fetch(Class<E> type, String name);

Mono<Unstructured> fetch(GroupVersionKind gvk, String name);

<E extends Extension> Mono<E> get(Class<E> type, String name);

/**
* Creates an Extension.
*
* @param extension is fresh Extension to be created. Please make sure the Extension name does
* not exist.
* @param <E> is Extension type.
*/
<E extends Extension> Mono<E> create(E extension);

/**
* Updates an Extension.
*
* @param extension is an Extension to be updated. Please make sure the resource version is
* latest.
* @param <E> is Extension type.
*/
<E extends Extension> Mono<E> update(E extension);

/**
* Deletes an Extension.
*
* @param extension is an Extension to be deleted. Please make sure the resource version is
* latest.
* @param <E> is Extension type.
*/
<E extends Extension> Mono<E> delete(E extension);
}

示例

如果你想在插件中根据 name 参数查询获取到 Person 自定义模型的数据,则可以这样写:

@Service
@RequiredArgsConstructor
public PersonService {
private final ReactiveExtensionClient client;

Mono<Person> getPerson(String name) {
return client.fetch(Person.class, name);
}
}

或者使用阻塞式 Client

@Service
@RequiredArgsConstructor
public PersonService {
private final ExtensionClient client;

Optional<Person> getPerson(String name) {
return client.fetch(Person.class, name);
}
}

注意:非阻塞线程中不能调用阻塞式方法。

我们建议你更多的使用响应式的 ReactiveExtensionClient 去替代 ExtensionClient

查询

ReactiveExtensionClient 提供了两个方法用于查询数据,listBylistAll

listBy 方法用于分页查询数据,listAll 方法用于查询所有数据,它们都需要一个 ListOptions 参数,用于传递查询条件:

public class ListOptions {
private LabelSelector labelSelector;
private FieldSelector fieldSelector;
}

其中 LabelSelector 用于传递标签查询条件,FieldSelector 用于传递字段查询条件。

FieldSelector 支持比自动生成的 APIs 中更多的查询条件,可以通过 run.halo.app.extension.index.query.QueryFactory 来构建。

ListOptions.builder()
.fieldQuery(QueryFactory.and(
QueryFactory.equal("name", "test"),
QueryFactory.equal("age", 18)
))
.build();

支持的查询条件如下:

方法说明示例
equal等于equal("name", "test"), name 是字段名,test 是字段值
equalOtherField等于其他字段equalOtherField("name", "otherName"), name 是字段名,otherName 是另一个字段名
notEqual不等于notEqual("name", "test")
notEqualOtherField不等于其他字段notEqualOtherField("name", "otherName")
greaterThan大于greaterThan("age", 18)
greaterThanOtherField大于其他字段greaterThanOtherField("age", "otherAge")
greaterThanOrEqual大于等于greaterThanOrEqual("age", 18)
greaterThanOrEqualOtherField大于等于其他字段greaterThanOrEqualOtherField("age", "otherAge")
lessThan小于lessThan("age", 18)
lessThanOtherField小于其他字段lessThanOtherField("age", "otherAge")
lessThanOrEqual小于等于lessThanOrEqual("age", 18)
lessThanOrEqualOtherField小于等于其他字段lessThanOrEqualOtherField("age", "otherAge")
in在范围内in("age", 18, 19, 20)
andand(equal("name", "test"), equal("age", 18))
oror(equal("name", "test"), equal("age", 18))
between在范围内between("age", 18, 20), 包含 18 和 20
betweenExclusive在范围内betweenExclusive("age", 18, 20), 不包含 18 和 20
betweenLowerExclusive在范围内betweenLowerExclusive("age", 18, 20), 不包含 18,包含 20
betweenUpperExclusive在范围内betweenUpperExclusive("age", 18, 20), 包含 18,不包含 20
startsWith以指定字符串开头startsWith("name", "test")
endsWith以指定字符串结尾endsWith("name", "test")
contains包含指定字符串contains("name", "test")
all指定字段的所有值all("age")

FieldSelector 中使用的所有字段都必须添加为索引,否则会抛出异常表示不支持该字段。关于如何使用索引请参考 自定义模型使用索引

可以通过 andor 方法组合和嵌套查询条件:

import static run.halo.app.extension.index.query.QueryFactory.and;
import static run.halo.app.extension.index.query.QueryFactory.equal;
import static run.halo.app.extension.index.query.QueryFactory.greaterThan;
import static run.halo.app.extension.index.query.QueryFactory.or;

Query query = and(
or(equal("dept", "A"), equal("dept", "B")),
or(equal("age", "19"), equal("age", "18"))
);
ListOptions.builder()
.fieldQuery(query)
.build();

构建 ListOptions

ListOptions 提供了 builder 方法用于构建查询条件,fieldQuery 方法用于传递字段查询条件,labelSelector 方法用于传递标签查询条件。

ListOptions.builder()
.labelSelector()
.eq("key-1", "value-1")
.end()
.fieldQuery(QueryFactory.equal("key-2", "value-2"))
.build();
  • labelSelector 之后使用 end 方法结束标签查询条件的构建。
  • andQueryorQuery 用于组合多个 FieldSelector 查询条件。

排序

listBylistAll 方法都支持传递 Sort 参数,用于传递排序条件。

import org.springframework.data.domain.Sort;

Sort.by(Sort.Order.asc("metadata.name"))

通过 Sort.by 方法可以构建排序条件,Sort.Order 用于指定排序字段和排序方式,asc 表示升序,desc 表示降序。

排序中使用的字段必须是添加为索引的字段,否则会抛出异常表示不支持该字段。关于如何使用索引请参考 自定义模型使用索引

分页

listBy 方法支持传递 PageRequest 参数,用于传递分页条件。

import run.halo.app.extension.PageRequestImpl;

PageRequestImpl.of(1, 10);

PageRequestImpl.of(1, 10, Sort.by(Sort.Order.asc("metadata.name"));

PageRequestImpl.ofSize(10);

通过 PageRequestImpl.of 方法可以构建分页条件,具有两个参数的方法用于指定页码和每页数量,具有三个参数的方法用于指定页码、每页数量和排序条件。

ofSize 方法用于指定每页数量,页码默认为 1。