Wicket LoadableDetachableModel - 在ajax请求期间不必要的分离

时间:2014-12-12 11:11:25

标签: ajax performance wicket

我注意到Wicket的LoadableDetachableModel(LDM)按设计分离了每个请求(通过RequestCycle.processRequestAndDetach())。这可能会在特定情况下导致性能问题,我希望保留缓存数据,同时仍然使用LDM的优势。

假设您有id的实体的详细信息页面。该页面分为选项卡(AjaxTabbedPanel)。 如果您打开页面,则从DB读取实体以获取给定的ID(进入LDM)。 如果单击第二个选项卡,模型已经分离,将再次重新加载。 在我的情况下,这不是必需的,因为我不想刷新每个请求的数据。

我想在页面上使用LDM,以便页面历史记录可用(只有实体ID将被序列化并按需重新加载数据)。

那么如何修复不必要的重载?

我想出了两个解决方案:

  1. 将LDM包装到静态模型中,这样它就不会自动分离,只在需要时手动分离(=重新加载)。
  2. 实现LDM的子实现,它将保留瞬态数据并仅在null时重新加载(如复活序列化页面时)。然后可以重复使用这个geenric模型。
  3. 我认为在wicket中已经有了一个解决方案,但我找不到任何解决方案。你知道其他(标准)方法吗?

    非常感谢您的回答。

    PS:2)的示例实现

        private class MyEntityModel extends LoadableDetachableModel<MyEntity> {
        private String entityId;
        private transient MyEntity modelObject;
    
        public MyEntityModel(String entityId) {
            this.entityId = entityId;
        }
    
        //call this for explicit reload
        public void forceDetach() {
            modelObject = null;
            detach();
        }
    
        @Override
        protected MyEntity load() {
            if (modelObject != null) {
                return modelObject;
            }
            MyEntity entity = getData(entityId);
            modelObject = message;
            return message;
        }
    }
    

    更新: 看来我原来的问题还不够明确。对不起。 目前的行为:

    1. wicket页面加载了作为参数传递的entityID
    2. 创建了一个新的LDM实例,传递给它的entityID
    3. 当(在响应呈现期间)调用LDM.getObject()时,实体从数据库加载
    4. 呈现响应后,调用LDM.detach()
    5. 加载了包含ajax控件的页面
    6. 当用户点击任何控件(如制表符开关)时,会向服务器生成新请求
    7. 服务器交换选项卡,尝试用数据填充新选项卡,因此它调用已在4中分离的LDM.getObject(),并再次加载数据
    8. 响应发送到客户端,LDM.detach()被调用
    9. 所需行为: Ad 4 - 调用LDM.detach()时,将保留瞬态模型对象 Ad 7 - 如果瞬态模型对象仍在内存中,请使用它。否则加载数据

      上面我描述了两种方法,如何实现这种行为。有没有标准或更好的方法?

3 个答案:

答案 0 :(得分:2)

有几种方法可以实现您的目标:

  1. 长时间运行的hibernate会话
  2. 将实体与会话或实体经理分离
  3. 使用CDI并将实体存储在会话范围对象中
  4. 我没有使用过1和2,但是2可能是你目前使用的最接近的替代品(并且更快地实现)。 3可能是您实际想要实现的最接近的替代方案,但需要您为应用程序添加一个全新的基础架构 - 取决于您的容器更容易(JavaEE7)或更难(一个普通的旧servlet容器,如Tomcat / Jetty)。 / p>

答案 1 :(得分:1)

  

我注意到Wicket的LoadableDetachableModel(LDM)在每个请求中都是按设计分离的(通过RequestCycle.processRequestAndDetach())。这可能会在特定情况下导致性能问题,我希望保留缓存数据,同时仍然使用LDM的优势。

您是否知道它实际上会导致性能问题?

听起来您正试图通过构建缓存机制来优化性能。如果实际上你确实遇到了问题并且需要这个问题,那么建立自己的缓存可能是一个更好的解决方案。

如果您正在使用hibernate,您可能希望将hibernate配置为使用Second Level Cache

如果您不使用休眠,那么 用于持久性的任何机制都可能具有类似的缓存。或者,如果您实际上需要添加自己的,则应该基于现有的缓存机制,例如ehcache

答案 2 :(得分:0)

大多数人都将使用ORM(JDO或JPA的实现),而最有用的用例是“开放式持久性管理器/会话视图”。相信我,它是最有用的-我已经尝试了很长时间,尝试了所有其他方式来避免出现这种情况,但是它们只是以眼泪和很多多余的错误代码结尾。

“在视图中打开持久性管理器/会话”意味着,当您的应用收到HTTP请求时,它会获得一个新的持久性管理器/会话(具有自己的L1缓存),然后您的代码根据需要使用它来加载对象,通过IModel向Wicket组件提供内容,然后Wicket组件分离在http请求周期中调用其onDetach方法时所拥有的模型引用。在请求结束时,持久性管理器/会话关闭。当然,我们丢失了L1高速缓存,但是它的大多数功能仍然保留在L2高速缓存中。

您有二级缓存吗?

任何好的ORM都将具有2级(L2)缓存,该缓存将缓存持久性管理器在先前的HTTP请求期间加载的所有对象(当然受内存容量限制)。

鉴于这些已经在内存中,因此可以将它们快速复制到为服务新HTTP请求而创建的新持久性管理器的L1缓存中。也就是说,如果对象位于L2缓存中,则ORM不必从数据库中重新加载对象。

运行没有配置L2缓存的ORM的任何人都需要回到ORM学校的第一天,第1.0.1。节。

当加载一个持久对象时,它将“附加”到加载它的持久性管理器。除非先将其与原始持久性管理器分离,然后再将其重新附加到新的持久性管理器,否则不要将其与新的持久性管理器一起使用。

这是Wicket的onDetach为您节省的地方-并且(最后回答了问题;))这就是为什么在每个HTTP请求的末尾都需要进行分离:这是您进行所有ORM分离的地方,并且必须在在每个请求结束时,否则您将在后续的HTTP请求上跨不同的持久性管理器对对象进行交叉授粉-结果总是很糟糕!