java jsf查询数据库_使用JSF/Java EE从数据库实时更新

前言

在这个答案中,我将假设:

>您不需要使用< p:push> (我会在中间留下确切的原因,您至少对使用新的Java EE 7 / JSR356 WebSocket API感兴趣)。

>您想要一个应用程序范围的推送(即所有用户一次获得相同的推送消息;因此您对会话不感兴趣,也不查看范围的推送)。

>你想直接从(MySQL)DB端调用push(因此你不用兴趣从使用实体侦听器的JPA端调用push)。编辑:我将覆盖两个步骤。步骤3a描述DB触发,步骤3b描述JPA触发。也可以使用它们,也可以不使用它们。

1.创建一个WebSocket端点

首先创建一个基本上将所有websocket会话收集到应用程序集的@ServerEndpoint类。请注意,在这个特定的例子中,这个例子只能是静态的,因为每个websocket会话基本上都有自己的@ServerEndpoint实例(它们不像servlet那样是无状态的)。

@ServerEndpoint("/push")

public class Push {

private static final Set SESSIONS = ConcurrentHashMap.newKeySet();

@OnOpen

public void onOpen(Session session) {

SESSIONS.add(session);

}

@OnClose

public void onClose(Session session) {

SESSIONS.remove(session);

}

public static void sendAll(String text) {

synchronized (SESSIONS) {

for (Session session : SESSIONS) {

if (session.isOpen()) {

session.getAsyncRemote().sendText(text);

}

}

}

}

}

上面的示例有一个附加的方法sendAll(),它将给定的消息发送到所有打开的websocket会话(即应用范围的推送)。请注意,这个消息也可以是一个JSON字符串。

如果您打算将它们显式存储在应用范围(或(HTTP)会话范围)中,那么可以使用this answer中的ServletAwareConfig示例。你知道,ServletContext属性映射到JSF中的ExternalContext#getApplicationMap()(和HttpSession属性映射到ExternalContext#getSessionMap())。

2.在客户端打开WebSocket,然后收听

使用这个JavaScript来打开一个websocket并且听它:

if (window.WebSocket) {

var ws = new WebSocket("ws://example.com/contextname/push");

ws.onmessage = function(event) {

var text = event.data;

console.log(text);

};

}

else {

// Bad luck. Browser doesn't support it. Consider falling back to long polling.

// See http://caniuse.com/websockets for an overview of supported browsers.

// There exist jQuery WebSocket plugins with transparent fallback.

}

到目前为止,它只记录推文本。我们想使用这个文本作为更新菜单组件的指令。为此,我们需要一个额外的< p:remoteCommand&gt ;.

想象一下,您将通过Push.sendAll(“updateMenu”)发送JS函数名称作为文本,然后可以解释并触发它,如下所示:

ws.onmessage = function(event) {

var functionName = event.data;

if (window[functionName]) {

window[functionName]();

}

};

再次,当使用JSON字符串作为消息(您可以通过$ .parseJSON(event.data)解析)时,更多的动态是可能的。

3A。从DB端触发WebSocket推送

现在我们需要从DB端触发Push.sendAll(“updateMenu”)命令。让DB在Web服务上触发HTTP请求的最简单的方式之一。一个简单的香草小服务器就足以像Web服务一样行事:

@WebServlet("/push-update-menu")

public class PushUpdateMenu extends HttpServlet {

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

Push.sendAll("updateMenu");

}

}

您当然有机会根据请求参数或路径信息参数化推送消息,如有必要。如果调用者被允许调用此servlet,请不要忘记执行安全检查,否则世界其他任何其他数据库本身就可以调用它。例如,您可以检查呼叫者的IP地址,如果DB服务器和Web服务器都在同一台机器上运行,那么该方法非常方便。

为了让DB在该servlet上触发HTTP请求,您需要创建一个可重用的存储过程,它基本上调用操作系统特定的命令来执行HTTP GET请求,例如。卷曲。 MySQL本身不支持执行特定于操作系统的命令,因此您需要先安装用户定义的函数(UDF)。在mysqludf.org你可以找到一堆SYS是我们的兴趣。它包含我们需要的sys_exec()函数。一旦安装,在MySQL中创建以下存储过程:

DELIMITER //

CREATE PROCEDURE menu_push()

BEGIN

SET @result = sys_exec('curl http://example.com/contextname/push-update-menu');

END //

DELIMITER ;

现在你可以创建insert / update / delete触发器来调用它(假设表名称是菜单):

CREATE TRIGGER after_menu_insert

AFTER INSERT ON menu

FOR EACH ROW CALL menu_push();

CREATE TRIGGER after_menu_update

AFTER UPDATE ON menu

FOR EACH ROW CALL menu_push();

CREATE TRIGGER after_menu_delete

AFTER DELETE ON menu

FOR EACH ROW CALL menu_push();

3B。或从JPA端触发WebSocket推送

如果您的需求/情况仅允许监听JPA实体更改事件,因此不需要覆盖对数据库的外部更改,则可以如步骤3a中所述而不是DB触发器,而只是使用JPA实体更改侦听器。您可以通过@Entity类注册@EntityListeners注释来注册它:

@Entity

@EntityListeners(MenuChangeListener.class)

public class Menu {

// ...

}

如果您碰巧使用单个Web配置文件项目,其中将所有内容(EJB / JPA / JSF)都放在同一个项目中,那么您可以直接在其中调用Push.sendAll(“updateMenu”)。

public class MenuChangeListener {

@PostPersist

@PostUpdate

@PostRemove

public void onChange(Menu menu) {

Push.sendAll("updateMenu");

}

}

然而,在“企业”项目中,服务层代码(EJB / JPA / etc)通常在EJB项目中分离,Web层代码(JSF / Servlets / WebSocket / etc)保留在Web项目中。 EJB项目应该具有no single对web项目的依赖。在这种情况下,您最好是启动一个CDI事件,而Web项目可以@Observes。

public class MenuChangeListener {

// Outcommented because it's broken in current GF/WF versions.

// @Inject

// private Event event;

@Inject

private BeanManager beanManager;

@PostPersist

@PostUpdate

@PostRemove

public void onChange(Menu menu) {

// Outcommented because it's broken in current GF/WF versions.

// event.fire(new MenuChangeEvent(menu));

beanManager.fireEvent(new MenuChangeEvent(menu));

}

}

(注意:在当前版本(4.1 / 8.2)中注入CDI事件在GlassFish和WildFly中被破坏;解决方法通过BeanManager触发事件;如果仍然不起作用,CDI 1.1替代方法是CDI.current ().getBeanManager()。fireEvent(new MenuChangeEvent(menu)))

public class MenuChangeEvent {

private Menu menu;

public MenuChangeEvent(Menu menu) {

this.menu = menu;

}

public Menu getMenu() {

return menu;

}

}

然后在web项目中:

@ApplicationScoped

public class Application {

public void onMenuChange(@Observes MenuChangeEvent event) {

Push.sendAll("updateMenu");

}

}

更新时间:2016年4月1日(上述答复后半年),OmniFaces推出了版本2.3.,这应该使这一切都不那么迂回。即将到来的JSF 2.3< f:websocket>主要基于< o:socket>。参见How can server push asynchronous changes to a HTML page created by JSF?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值