1

假设我有一个名为Customer. Customer可以在应用程序中创建、删除或编辑对象。

我创建了一个表示 列表的复合组件Customer,以便我可以在我的应用程序的多个位置重用它。

<!-- INTERFACE -->
<cc:interface>
    <cc:attribute name="val" required="true"/>
</cc:interface>

<!-- IMPLEMENTATION -->
<cc:implementation>
    <p:selectOneMenu value="val">
        <f:selectItems value="#{appManager.customers}"
                       var="cust"
                       itemLabel="#{cust.name}"/>
    </p:selectOneMenu>
</cc:implementation>

@ApplicationScope该组件使用 EJB绑定到托管 bean。

@Named
@ApplicationScoped
public class AppManager {
    @EJB
    private CustomerFacade customerFacade;

    public AppManager() {
    }

    public List<Customer> customers(){
        return customerFacade.findAll();
    }
}

但是每次使用这个组件时,Customer都会获取表格,对吗?我怎样才能Customer更有效地检索这个集合?我想过使用集合的延迟加载:

@Named
@ApplicationScoped
public class AppManager {

    @EJB
    private CustomerFacade customerFacade;
    private List<Customer> customers;

    /**
     * Creates a new instance of AppManager
     */
    public AppManager() {
    }

    public List<Customer> getCustomers() {
        if(customers == null){
            customers = customerFacade.findAll();
        }
        return customers;
    }
}

但是随后应用于数据库的更改不会反映在集合上。

对于这种情况下的常用技术或最佳实践,我将不胜感激。谢谢 :)

4

3 回答 3

0

用法视情况而定。

如果数据不经常更改,那么您执行它的方式(除了在@PostConstruct方法中初始化是明智之举)似乎很好。将其视为初始化应用程序默认值,例如国家列表。您当然可以进行一些检查以查看常量是否已自动/手动更改以更新内容。

如果数据不断变化/更新,那么您获取数据的方式非常低效,因为您不应该在应用程序范围内缓存数据。我能想到的最有效的方法是将该数据绑定到一个@ViewScopedbean 实例并实现数据的分页,以防有很多需要(预)加载,或者通过查询参数来完成,就像你的客户一样。当然,如果要加载的项目很少,您可以获取所有数据,例如特定顺序的项目列表。

总而言之,我将通过以下方式实施您的解决方案:

  1. 创建一个包含当前页面数据(客户子集,例如 20 个)和当前分页状态(当前页面、下一页/上一页/总页等)的视图范围 bean;
  2. 提供在视图中切换到另一个数据子集并相应更新 bean 数据的能力;
  3. 在您的 EJB 中引入一个新方法,在 旁边findAll,例如public List<Customer> find(int from, int to).

您也可以通过<f:param>/<f:viewParam>进行通信,以保持 URL 可收藏。

于 2013-10-10T08:30:50.733 回答
0

你的问题的答案:

但是每次使用这个组件时,都会获取 Customer 表,对吗?

是:取决于。假设您正在使用容器管理的持久性上下文,它可能是或否取决于您在 persistence.xml 文件中定义的持久性提供程序的配置(或您的提供程序的默认值)。如果您使用 EclipseLink,则默认启用对象缓存(与对象 ID 相关),并且可能会取消激活查询缓存,将此属性添加到您的 persistence.xml 的 persistence-unit 部分:

<properties>
  <property name="eclipselink.query-results-cache" value="true"/>
</properties>

很容易检查它是否在缓存上工作。部署您的应用程序并进行查询 findAll()。在此之后,直接在数据库中插入一个新客户并使用 findAll() 查询重新加载所有客户。我很确定不会加载新客户,这表明持久性管理器使用它的缓存来返回结果。

恕我直言,最好的方法是让容器管理持久性上下文。因此,在您的情况下,保留您的前端代码,只需在您的 CustomerFacade 中添加一个服务以在您的数据库中插入(CRUD)客户,这样,新客户将自动反映在缓存中(这是容器管理的持久性单元的魔力!)。

于 2013-10-14T18:56:54.073 回答
0

skuntsel 有一些非常好的观点,所以我只会提出替代方案。

我会将客户缓存移动到customerFacade. 然后我会:

  1. 确保 List 是一个 volatile readonly list 或一个copyonwritearraylist(只要它不经常更改
  2. 向您的 EJB 添加挂钩方法,在添加/删除/编辑值时更新列表
  3. 确保列表数据是只读的,因为它们将在线程之间共享。

不要忘记大多数 JPA 实现也会缓存实体(您也可以使用 3rd 方缓存设置它们),因此操作可能比您预期的要快。

如果数据非常小,我会寻求 skuntsel 建议并使用@ViewScopedbean。它更容易,更简单,更简洁,并且假设您使用 Ajax,则只会遭受一次打击。

于 2013-10-11T09:19:58.290 回答