如何有效地从数据库中检索数据以填充selectonemenu

时间:2013-10-10 07:50:53

标签: java jsf-2 jpa-2.0 ejb-3.1 jsf-2.2

假设我有一个名为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>

此组件使用EJB绑定到@ApplicationScope托管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;
    }
}

但是,应用于数据库的更改不会反映在集合中。

我非常感谢有关这种情况下的常用技术或最佳做法的一些建议。谢谢:))

3 个答案:

答案 0 :(得分:0)

用法取决于具体情况。

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

如果数据受到不断变化/更新的影响,那么获取数据的方式效率非常低,因为您不应该在应用程序范围内缓存数据。我能想到的最有效的方法是将数据绑定到@ViewScoped bean实例和implement pagination of data,以防有多少(预)加载,或通过查询参数执行,如在客户的案例。当然,如果要加载的项目很少,您可以获取所有数据,例如特定订单中的项目列表

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

  1. 创建一个视图范围的bean,其中包含当前页面数据(一个custoimers的子集,比如20),以及当前的分页状态(当前页面,下一个/上一个/总页面等);
  2. 提供在视图中切换到另一个数据子集并相应更新bean数据的功能;
  3. 在您的EJB中引入一个新方法,在findAll旁边,例如public List<Customer> find(int from, int to)
  4. 您也可以通过<f:param> / <f:viewParam>进行通讯,以确保网址可收藏。

答案 1 :(得分:0)

skuntsel有一些非常好的观点,所以我只是建议替代方案。

我会在customerFacade中移动客户缓存。然后我会:

  1. 确保列表是易失性只读列表或copyonwritearraylist(前提是它不会经常更改
  2. 向EJB添加挂钩方法,以便在添加/删除/编辑值时更新列表
  3. 确保列表数据是只读的,因为它们将在线程间共享。
  4. 不要忘记大多数JPA实现也会缓存实体(并且您也可以使用第三方缓存设置它们),因此操作可能比您预期的要快。

    如果数据非常小,我会选择skuntsel建议并使用@ViewScoped bean。它更容易,更简单,更清晰,并且假设您使用Ajax,命中只会遭受一次。

答案 2 :(得分:0)

你的问题的答案:

  

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

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

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

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

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