集合实例容器

CollectionContainer 接口用来容纳相同类型实例的集合。这个接口继承自 InstanceContainer

在 XML 描述中可以这样定义 CollectionContainer

<data readOnly="true">
    <collection id="customersDc"
                class="ui.ex1.entity.Customer">
        <fetchPlan extends="_base"/>
        <loader id="customersDl" >
            <query>
                <![CDATA[select e from uiex1_Customer e]]>
            </query>
        </loader>
    </collection>
</data>

方法

CollectionContainer 定义了下列特殊方法:

  • setItems() - 为容器设置实体集合。

  • getItems() - 返回容器中保存的实体的不可变列表。可以用这个方法来遍历集合、获得实体流或者根据索引获取单一实例。如果需要按照实体的 id 获取实例,使用 getItem(entityId) 方法。示例:

    @Autowired
    private CollectionContainer<Customer> customersDc;
    
    private Optional<Customer> findByName(String name) {
        return customersDc.getItems().stream()
                .filter(customer -> Objects.equals(customer.getLastName(), name))
                .findFirst();
    }
  • getMutableItems() - 返回容器中保存的实体的可变列表。所有对列表的改动,包括 add()addAll()remove()removeAll()set()clear() 方法都会产生 CollectionChangeEvent 事件,所以订阅了这个事件的可视化组件也会根据变化更新,示例:

    @Autowired
    private CollectionContainer<Customer> customersDc;
    
    private void createCustomer() {
        Customer customer = metadata.create(Customer.class);
        customer.setFirstName("John");
        customer.setLastName("Doe");
        customersDc.getMutableItems().add(customer);
    }
    只有在需要更改集合的时候使用 getMutableItems(),否则应该使用 getItems(),防止意外改动。
  • setItem() - 为容器设置当前实例。如果提供的内容不是 null,则必须是集合中的一个对象。此方法会发送 ItemChangeEvent

    需要注意的是,类似 Table 的可视化组件不会监听容器发送的 ItemChangeEvent 事件。所以如果需要在表中选中一行,需要使用集合容器的 setSelected() 方法,而不是 setItem()。容器的当前 item 也会更改,因为容器同时也监听了组件。示例:

    @Autowired
    private CollectionContainer<Customer> customersDc;
    
    @Autowired
    private GroupTable<Customer> customersTable;
    
    private void selectFirstRow() {
        customersTable.setSelected(customersDc.getItems().get(0));
    }
  • getItem() - 重写了 InstanceContainer 的同名方法,返回当前实例。如果当前实例没有设置,此方法会抛出一个异常。所以需要在确保容器有选中当前实例的时候才使用此方法,然后就不需要检查返回值是否为 null

  • getItemOrNull() - 重写了 InstanceContainer 的同名方法,返回当前实例。如果当前实例没有设置,此方法会返回 null。所以在使用此方法返回值之前总是需要先检查返回的是否是 null

  • getItemIndex(entityId) - 返回实例在 getItems()getMutableItems() 方法返回的列表中的位置。此方法接收 Object 对象,因此可以传给它 id 或者实体实例本身。容器的实现维护了一个 id 到索引的映射,所以这个方法即使在非常大的列表中也有很高效率。

  • getItem(entityId) - 按照实例的 id 返回此实例。这个是一个快捷方法,首先用 getItemIndex(entityId) 得到实例的位置,然后通过 getItems().get(index) 返回实例。所以如果需要找的实例不在集合中存在,则会抛出异常。

  • getItemOrNull(entityId) - 跟 getItem(entityId) 类似,只不过在实例不存在的时候会返回 null。所以需要在使用前检查此方法的返回值是否是 null

  • containsItem(entityId) - 如果指定 id 的实体在集合中存在的话,返回 true。底层其实调用了 getItemIndex(entityId) 方法。

  • replaceItem(entity) - 如果在容器中有相同 id 的实例,则会被方法的输入参数的实例替换。如果不存在,则会添加新的实例到实例列表中。此方法会发送 CollectionChangeEvent 事件,根据具体的操作,事件类型可以是 SET_ITEM 或者 ADD_ITEMS

  • setSorter() - 设置此容器的排序器。Sorter 接口的标准实现是 CollectionContainerSorter。当容器关联到加载器时,会设置默认的排序器。如果需要,也可提供 自定义实现

  • getSorter() - 返回此容器当前设置的排序器。

事件

除了 InstanceContainer 的事件之外,还可以使用 CollectionContainer 接口的 CollectionChangeEvent 事件的监听器,该事件在容器内的实体集合改动时发送,比如,添加、删除和替换集合内元素。下面例子订阅了该事件,容器在界面 XML 中使用 id customersDc 定义:

@Subscribe(id = "customersDc", target = Target.DATA_CONTAINER)
public void onCustomersDcCollectionChange(CollectionContainer.CollectionChangeEvent<Customer> event) {
    CollectionChangeType changeType = event.getChangeType(); (1)
    Collection<? extends Customer> changes = event.getChanges(); (2)
    // ...
}
1 获取改动类型:REFRESHADD_ITEMSREMOVE_ITEMSSET_ITEM
2 从容器中添加或者删除的实体集合。如果改动类型是 REFRESH,框架不能确定具体是哪些实体添加或者删除,所以此时该集合为空。