我有一个应用程序在下面的环境中运行。
- 服务器4.0
- JSF 2.2.8-02标准
- PrimeFaces 5.1最终版
- PrimeFaces扩展模块2.1.0
- 全方位工作面1.8.1
- 包含JPA 2.1的Eclipse链接2.5.2
- MySQL 5.6.11
- JDK-7 u11语言
有几个公共页面是从数据库懒洋洋地加载的。一些CSS菜单显示在模板页面的标题上,如显示类别/子类别的特色,畅销,新到达等产品。
基于数据库中的各种产品类别从数据库动态填充CSS菜单。
这些菜单在每次页面加载时填充,这是完全不必要的。其中一些菜单需要复杂/昂贵的JPA条件查询。
目前,填充这些菜单的JSF托管bean都是视图范围的,它们都应该是应用程序范围的,只在应用程序启动时加载一次,并且只在相应的数据库表(类别/子类别/产品等)中的某些内容更新/更改时才更新。
我做了一些尝试来理解WebSokets(以前从未尝试过,对WebSokets来说是全新的),比如this和this。它们在GlassFish 4.0上运行良好,但它们不涉及数据库。我仍然无法正确理解WebSokets是如何工作的。尤其是涉及数据库的时候。
在这种情况下,如何通知相关联的客户端,并更新上述CSS菜单与最新的值从数据库,当一些更新/删除/添加到相应的数据库表?
一个简单的例子/s会很棒。
3条答案
按热度按时间pgpifvop1#
前言
在回答这个问题时,我会假设以下几点:
<p:push>
不感兴趣(我将把确切的原因放在中间,您至少对使用新的Java EE 7 /JSR 356 WebSocket API感兴趣)。1.创建WebSocket端点
首先创建一个
@ServerEndpoint
类,它基本上将所有的WebSocket会话收集到一个应用程序范围的集合中。注意,在这个特定的例子中,它只能是static
,因为每个websocket会话基本上都有自己的@ServerEndpoint
示例(它们不像servlet,因此是无状态的)。上面的例子有一个额外的方法
sendAll()
,它将给定的消息发送到所有打开的WebSocket会话(即应用程序范围的推送)。注意,这个消息也可以是一个JSON字符串。如果您打算显式地将它们存储在应用程序范围(或(HTTP)会话范围)中,那么您可以使用此答案中的
ServletAwareConfig
示例。您知道,ServletContext
属性Map到JSF中的ExternalContext#getApplicationMap()
(而HttpSession
属性Map到ExternalContext#getSessionMap()
)。2.在客户端打开WebSocket并监听
使用这段JavaScript打开一个WebSocket并监听它:
到目前为止,它只记录推送的文本。我们希望使用此文本作为更新菜单组件的指令。为此,我们需要一个额外的
<p:remoteCommand>
。假设您通过
Push.sendAll("updateMenu")
发送文本形式的JS函数名,那么您可以如下所示解释并触发它:同样,当使用JSON字符串作为消息(可以通过
$.parseJSON(event.data)
解析)时,可以实现更多的动态性。3a. * 或者 * 从数据库端触发WebSocket推送
现在我们需要从数据库端触发命令
Push.sendAll("updateMenu")
。最简单的方法之一是让数据库在Web服务上触发HTTP请求。一个普通的servlet足以像Web服务一样工作:当然,如果需要,您可以根据请求参数或路径信息来参数化推送消息。如果允许调用者调用此servlet,请不要忘记执行安全检查,否则除了DB本身之外,世界上的任何其他人都可以调用它。您可以检查调用者的IP地址,例如,如果DB服务器和Web服务器在同一台机器上运行,这将非常方便。
为了让DB在servlet上触发HTTP请求,您需要创建一个可重用的存储过程,它基本上调用操作系统特定的命令来执行HTTP GET请求,例如
curl
。MySQL本身不支持执行操作系统特定的命令。所以你需要安装一个用户定义的函数(UDF)。在mysqludf.org中你可以找到一堆我们感兴趣的SYS。它包含了我们需要的sys_exec()
函数。安装后,在MySQL中创建以下存储过程:现在可以创建调用它的插入/更新/删除触发器(假设表名为
menu
):3b. * 或 * 从JPA端触发WebSocket推送
如果您的需求/情况允许只监听JPA实体更改事件,因此不需要覆盖DB的外部更改,那么您可以而不是步骤3a中描述的DB触发器,也可以只使用JPA实体更改监听器。您可以通过
@Entity
类上的@EntityListeners
注解注册它:如果您碰巧使用一个Web概要文件项目,其中所有内容(EJB/JPA/JSF)都放在同一个项目中,那么您可以直接在其中调用
Push.sendAll("updateMenu")
。然而,在“企业”项目中,服务层代码(EJB/JPA/etc)通常在EJB项目中分离,而Web层代码(JSF/Servlet/WebSocket/etc)保留在Web项目中。EJB项目应该对Web项目具有no single依赖性。在这种情况下,您最好启动CDI
Event
,而Web项目可能会@Observes
。Event
的注入;变通方案改为通过BeanManager
激发事件;如果这仍然不起作用,CDI 1.1替代方案是CDI.current().getBeanManager().fireEvent(new MenuChangeEvent(menu))
)*然后在web项目中:
更新:2016年4月1日(上述回答半年后),OmniFaces在2.3版本中引入了
<o:socket>
,这将使这一切变得更简单。即将到来的JSF 2.3<f:websocket>
主要基于<o:socket>
。另请参见How can server push asynchronous changes to a HTML page created by JSF?bq9c1y662#
由于您使用的是Primefaces和Java EE 7,因此应该很容易实现:
使用质数推进(此处示例为http://www.primefaces.org/showcase/push/notify.xhtml)
希望这能有所帮助,如果你需要更多的细节,请询问
问候
fnvucqvd3#
PrimeFaces具有自动更新组件的轮询功能。在以下示例中,
<h:outputText>
将每3秒自动更新一次。如何通知相关的客户端并使用数据库中的最新值更新上述CSS菜单?
创建一个类似
process()
的侦听器方法来选择菜单数据。<p:poll>
将自动更新菜单组件。