跳至主要内容

仓库 24.00

在 ChatGPT 中打开

Repository模式在webforJ中提供了一种标准化的方式来管理和查询实体的集合。它充当了用户界面组件和数据之间的抽象层,使得处理不同数据源的工作变得简单,同时保持一致的行为。

为什么使用仓库

Repository消除了手动更新的需要,同时保持原始数据不变:

// 没有 Repository - 手动更新
List<Customer> customers = loadCustomers();
Table<Customer> table = new Table<>();
table.setItems(customers);

// 添加需要完全重新加载
customers.add(newCustomer);
table.setItems(customers); // 必须重新加载所有内容
// 使用 Repository - 自动同步
List<Customer> customers = loadCustomers();
CollectionRepository<Customer> repository = new CollectionRepository<>(customers);
Table<Customer> table = new Table<>();
table.setRepository(repository);

// 添加会自动同步
customers.add(newCustomer);
repository.commit(newCustomer); // 只更新更改的部分

集合仓库

CollectionRepository是最常见的实现,并包装任何Java集合:

// 从 ArrayList
List<Customer> customers = new ArrayList<>();
CollectionRepository<Customer> customerRepo = new CollectionRepository<>(customers);

// 从 HashSet
Set<String> tags = new HashSet<>();
CollectionRepository<String> tagRepo = new CollectionRepository<>(tags);

// 从任何集合
Collection<Employee> employees = getEmployeesFromHR();
CollectionRepository<Employee> employeeRepo = new CollectionRepository<>(employees);

数据同步

Repository充当了数据和用户界面组件之间的桥梁。当数据发生变化时,您通过commit()方法通知仓库:

List<Product> products = new ArrayList<>();
CollectionRepository<Product> repository = new CollectionRepository<>(products);

// 添加新产品
Product newProduct = new Product("P4", "小工具", 79.99, 15);
products.add(newProduct);
repository.commit(); // 所有连接的组件更新

// 更新现有产品
products.get(0).setPrice(89.99);
repository.commit(products.get(0)); // 只更新这一特定行

// 删除产品
products.remove(2);
repository.commit(); // 刷新视图

commit方法有两个签名:

  • commit() - 告诉仓库刷新所有内容。它触发一个RepositoryCommitEvent与所有当前数据
  • commit(entity) - 目标是特定实体。仓库通过其键查找该实体,并仅更新受影响的UI元素
提交单个实体

这个区分对性能很重要。当您在一个1000行的表中更新一个字段时,commit(entity)只更新那个单元格,而commit()将刷新所有行。

过滤数据

仓库的过滤器控制哪些数据流向连接的组件。您的底层集合保持不变,因为过滤器充当一个透镜:

// 按库存可用性过滤
repository.setBaseFilter(product -> product.getStock() > 0);

// 按类别过滤
repository.setBaseFilter(product -> "电子产品".equals(product.getCategory()));

// 组合多个条件
repository.setBaseFilter(product ->
product.getCategory().equals("电子产品") &&
product.getStock() > 0 &&
product.getPrice() < 100.0
);

// 清除过滤器
repository.setBaseFilter(null);

当您设置过滤器时,Repository

  1. 将谓词应用于您集合中的每个项目
  2. 创建一个匹配项的过滤后的流
  3. 通知连接的组件更新其显示

过滤器在您更改之前会持续存在。添加到集合的新项目会自动通过当前过滤器进行测试。

使用实体键

仓库需要唯一标识实体,以支持诸如find()commit(entity)之类的操作。有两种方法可以定义实体的标识方式:

使用 HasEntityKey 接口

在您的实体类上实现HasEntityKey

public class Customer implements HasEntityKey {
private String customerId;
private String name;
private String email;

@Override
public Object getEntityKey() {
return customerId;
}

// 构造函数和getter/setter...
}

// 通过键查找
Optional<Customer> customer = repository.find("C001");

// 更新特定客户
customer.ifPresent(c -> {
c.setEmail("newemail@example.com");
repository.commit(c); // 只更新此客户的行
});

使用自定义键提供者 25.10

对于您无法或不想实现HasEntityKey的实体(如JPA实体),使用setKeyProvider()

@Entity
public class Product {
@Id
private Long id;
private String name;
private double price;

// JPA管理的实体
}

// 配置仓库以使用getId()方法
CollectionRepository<Product> repository = new CollectionRepository<>(products);
repository.setKeyProvider(Product::getId);

// 现在通过ID查找
Optional<Product> product = repository.find(123L);

选择一种方法

这两种方法都可以工作,但setKeyProvider()在以下情况下优先使用:

  • 使用具有@Id字段的JPA实体
  • 您无法修改实体类
  • 您需要不同的键策略用于不同的仓库

在以下情况下使用HasEntityKey

  • 您控制实体类
  • 键提取逻辑复杂
  • 您希望实体定义其自己的身份

用户界面集成

Repository与数据感知组件集成:

// 创建仓库和表
List<Customer> customers = new ArrayList<>();
CollectionRepository<Customer> repository = new CollectionRepository<>(customers);

Table<Customer> table = new Table<>();
table.setRepository(repository);
table.addColumn("ID", Customer::getId);
table.addColumn("Name", Customer::getName);
table.addColumn("Email", Customer::getEmail);

// 添加数据 - 表自动更新
customers.add(new Customer("C001", "爱丽丝·约翰逊", "alice@example.com"));
repository.commit();

下一步