获取数据库条目的正确代码模式是什么?

发布于 2024-12-10 13:53:18 字数 2548 浏览 0 评论 0原文

在另外两个问题中(此处此处)BalusC 做出了直接声明:

获取器仅用于访问 bean 属性,而不是执行某些业务逻辑。那里有 bean 构造函数、初始化块或事件方法。所有这些在 bean 的生命周期中只执行一次,而这正是您想要的。

哎呀——这让我已经编写的无数行代码失效了。好吧,那么,实现填充数据表的支持 bean 的正确方法是什么?我理解他的观点和概念,但不理解实践。我的问题是双重的:

  1. 为什么我做事的方式是错误的?
  2. 我该如何修复它?

我经常使用 PrimeFaces p:dataTable,它的 value 属性解析为一个集合。由于我不在这里讨论的原因,我使用PrimeFaces的延迟表加载功能。相反,我实现了自己的过滤/排序控件,但它们会触发 AJAX 事件,从而导致表中填充从数据库获取的记录。

该表的标记如下:

 <p:panel id="mqTable">

   <h:outputText value="Sort/Filter: #{maintCategory.tableQueryParameters}" />

   <p:dataTable
      id="mqDataTable"
      rows="#{maintCategory.pageSize}"
      value="#{maintCategory.dataModel}"
      selection="#{maintCategory.selected}"
      var="cat"
      selectionMode="single"
      emptyMessage="No Categories Found">

现在 dataModel 的极其糟糕的 UN-JSFish(或者我刚刚发现的) getter 如下所示:

public ATMDataModel getDataModel() {
    TableQueryParameters p = getTableQueryParameters();
    if (p.isChangePending()) clearDataModel();
    p.setChangePending(false);
    if (dataModel != null) return dataModel;
    List<ET> list = getDAO().runQuery(p);
    if (p.isNeedResultSize()) p.setResultSize(getDAO().runQueryCount(p));
    dataModel = new ATMDataModel(list);
    return dataModel;
}

一些解释。

  1. 这是来自一个抽象超类,其中 ET 是“实体类型”。我所有的 CRUD 都使用相同的例程。
  2. ATMDataModel 类是实现 SelectableListModel 的列表的包装器。 PrimeFaces 中的行选择逻辑需要这样做。 (这是 PF 3 中出现的一个问题,但它使行选择工作更加可靠。)
  3. TableQueryParameters 类是我编写的,用于封装用户屏幕上表的当前状态。它包括什么排序参数、什么过滤器参数、我们在哪个页面等。因为需要保留这些,所以支持 bean 是 ViewAccesScoped (通过 MyFaces CODI)和 TableQueryParameters strong> 是其中的一个属性。
  4. TableQueryParameters 通过 AJAX 事件进行响应更新,这也会更新表单,从而导致调用 getDataModel。当发生任何变化时,isChangePending 方法将变为 true。因此,getDataModel 方法使用此方法在更改之间仅生成一次从 DAO 获取的数据,无论调用多少次。

但是如果TableQueryParameters确实发生变化,我必须使用这些参数调用runQuery来获取新的记录集用户想看。如果我不在 getDataModel 中调用它,我应该在哪里调用它?

请指教。

In two other questions (here and here) BalusC makes a straight up declaration:

Getters are solely there to access bean properties, not to do some business logic. There you have the bean constructor, initialization blocks or event methods for. All are executed only once during bean's life and that's exactly what you want.

Well gee -- this just invalidated a gazillion lines of code I have already written. Ok, then, what is the correct way of implementing a backing bean that fills a data table? I understand his point and the concept, but not the practice. My question is twofold:

  1. Why is the way I am doing it wrong?
  2. How do I fix it?

I use PrimeFaces p:dataTable a lot, and it's value attribute resolves to a collection. For reasons I don't go into here, I do not use PrimeFaces' lazy table loading feature. Instead I implement my own filter/sort controls, but they trigger AJAX events, which then results in the table being filled with records fetched from the data base.

The table is marked up like this:

 <p:panel id="mqTable">

   <h:outputText value="Sort/Filter: #{maintCategory.tableQueryParameters}" />

   <p:dataTable
      id="mqDataTable"
      rows="#{maintCategory.pageSize}"
      value="#{maintCategory.dataModel}"
      selection="#{maintCategory.selected}"
      var="cat"
      selectionMode="single"
      emptyMessage="No Categories Found">

Now the INCREDIBLY BAD UN-JSFish (or so I just found out) getter for dataModel goes like this:

public ATMDataModel getDataModel() {
    TableQueryParameters p = getTableQueryParameters();
    if (p.isChangePending()) clearDataModel();
    p.setChangePending(false);
    if (dataModel != null) return dataModel;
    List<ET> list = getDAO().runQuery(p);
    if (p.isNeedResultSize()) p.setResultSize(getDAO().runQueryCount(p));
    dataModel = new ATMDataModel(list);
    return dataModel;
}

A few explanations.

  1. This is from an abstract super-class where ET is the "Entity Type." All my CRUDs use this same routine.
  2. The class ATMDataModel is a wrapper for the list which implements SelectableListModel. The row selection logic in PrimeFaces requires this. (It is a pain that appeared in PF 3 but it makes row selection work more reliably.)
  3. The class TableQueryParameters is something I wrote to encapsulate the current state of the table on the user's screen. It includes what sort parameters, what filter parameters, what page we are on, etc. Because this needs to be preserved, the backing bean is ViewAccesScoped (via MyFaces CODI) and the TableQueryParameters is a property within it.
  4. The TableQueryParameters are updated in response via AJAX events, which also update the form causing getDataModel to be called. The method isChangePending goes true when anything changes. So the getDataModel method uses this to generate only one fetch from the DAO between changes no matter how many times it is called.

BUT if the TableQueryParameters do change, I have to call runQuery with those parameters to fetch the new set of records the user wants to see. If I don't call it in getDataModel where do I call it?

Please advise.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

孤芳又自赏 2024-12-17 13:53:18

您基本上是在 getter 中延迟加载数据。您不会在同一请求(或视图)范围内的每个 getter 调用中访问数据库。这是实惠的。我不使用 CODI,但我想 getTableQueryParameters() 调用也特别便宜,无需担心。

至于具体问题,您通常会在附加到 UICommand 组件和/或 ajax 事件标记的操作(侦听器)方法中执行数据库/业务工作。

例如(也可以用作

<p:ajax listener="#{bean.deleteSelectedRow}" />
...
<p:ajax listener="#{bean.saveRowDetail}" />

根据

public void deleteSelectedRow() {
    someService.delete(selectedRow);
    dataModel = loadDataModel();
}

public void saveRowDetail() {
    someService.save(selectedRow);
    dataModel = loadDataModel();
}

p.isChangePending() 的含义,我认为您也可以摆脱它这样,看起来您是在操作(侦听器)方法中设置它。

You're basically lazily loading the data in the getter. You're not hitting the DB on every getter call within the same request (or view) scope. This is affordable. I don't use CODI, but I'd imagine that the getTableQueryParameters() call is also particularly cheap and nothing to worry about.

As to the concrete question, you'd normally do the DB/business job in an action(listener) method which is attached to the UICommand component and/or the ajax event tag.

For example (works also as <p:commandButton action> though)

<p:ajax listener="#{bean.deleteSelectedRow}" />
...
<p:ajax listener="#{bean.saveRowDetail}" />

with

public void deleteSelectedRow() {
    someService.delete(selectedRow);
    dataModel = loadDataModel();
}

public void saveRowDetail() {
    someService.save(selectedRow);
    dataModel = loadDataModel();
}

Depending on the meaning of p.isChangePending(), I think you could also get rid of it this way, it look like that you were setting it in the action(listener) methods.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文