【问题标题】:How to efficiently retrieve data from DB to populate selectonemenu如何有效地从数据库中检索数据以填充 selectonemenu
【发布时间】:2013-10-17 20:57:39
【问题描述】:

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

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

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

【问题讨论】:

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


    【解决方案1】:

    你的问题的答案:

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

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

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

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

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

    【讨论】:

    • 我很确定容器会缓存我的实体,但我不知道eclipselink.query-results-cache,谢谢
    • 有时你确实需要绕过缓存。在这种情况下,您可以使用(如果您使用 EclipseLink 提供程序)强制查询不检查缓存: query.setHint("eclipselink.cache-usage", "DoNotCheckCache");在执行查询之前使用此代码。通过这种方式,您可以强制查询的特定运行直接从数据库中获取数据(它适用于命名查询和本机查询)。
    【解决方案2】:

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

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

    1. 确保列表是易失性只读列表或copyonwritearraylist(前提是它不经常更改
    2. 向您的 EJB 添加挂钩方法,以便在添加/删除/编辑值时更新列表
    3. 确保列表数据是只读的,因为它们将在线程间共享。

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

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

    【讨论】:

    • 我想我确实会像@skuntsel 建议的那样使用@ViewScoped,并相信容器可以缓存实体。
    【解决方案3】:

    使用情况视情况而定。

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

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

    总而言之,我会按照以下方式实施您的解决方案:

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

    您也可以通过&lt;f:param&gt;/&lt;f:viewParam&gt; 进行交流,以保持网址可收藏。

    【讨论】:

    • 我用Customer 选了一个错误的例子,我的数据集可能改变,但不会改变那么大大,所以分页不会有用。 您当然可以做一些检查来查看常量是否自动/手动更改以更新内容:您能否更具体或举个例子?
    猜你喜欢
    • 1970-01-01
    • 2012-02-20
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 2013-03-19
    • 1970-01-01
    相关资源
    最近更新 更多