Echtzeit-Updates von der Datenbank mit JSF / Java EE
Ich habe eine Anwendung, die in der folgenden Umgebung.
- GlassFish Server 4.0
- JSF-2.2.8-02
- PrimeFaces 5.1 final
- PrimeFaces Erweiterung 2.1.0
- OmniFaces 1.8.1
- EclipseLink 2.5.2 mit JPA 2.1
- MySQL-5.6.11
- JDK-7u11
Gibt es mehrere öffentliche Seiten, die träge aus der Datenbank geladen. Ein paar CSS-Menüs angezeigt werden die Kopfzeile der Vorlage Seite wie anzeigen Kategorie/Unterkategorie-Weise "featured", " top-Verkäufer, neue Ankunft, etc Produkte.
Den CSS-Menüs aufgefüllt werden dynamisch aus der Datenbank, basierend auf verschiedenen Kategorien von Produkten in der Datenbank.
Diese Menüs sind aufgefüllt, die auf jeder Seite geladen werden, die vollkommen unnötig ist. Einige dieser Menüs erfordern-Komplex/teuer JPA Kriterien Abfragen.
Derzeit die JSF-managed beans, bevölkern diese Menüs Ansicht beschränkt. Sie alle sollten application scoped, nur einmal geladen werden, die auf Anwendung starten und werden nur aktualisiert, wenn etwas in der entsprechenden Datenbank-Tabellen (Kategorie/Unterkategorie/Produkt etc.) ist aktualisiert/geändert.
Machte ich einige versuche um zu verstehen, WebSokets (nie versucht, vor, völlig neue WebSokets) wie diese und diese. Sie arbeiteten gut auf GlassFish 4.0 aber Sie beziehen nicht mit Datenbanken. Ich bin noch nicht in der Lage zu verstehen, richtig wie WebSokets Arbeit. Vor allem, wenn Datenbank beteiligt ist.
In diesem Szenario, wie zu Benachrichtigen, die damit verbundenen clients und aktualisieren Sie die oben genannten CSS-Menüs mit den neuesten Werten aus der Datenbank, wenn etwas aktualisiert/gelöscht/Hinzugefügt, um die entsprechenden Datenbank-Tabellen?
Ein einfaches Beispiel/s groß sein.
InformationsquelleAutor der Frage Tiny | 2014-09-20
Du musst angemeldet sein, um einen Kommentar abzugeben.
Vorwort
In dieser Antwort nehme ich an, dass die folgenden:
<p:push>
(ich lasse den genauen Grund in der Mitte, du bist wenigstens daran interessiert, mit der neuen Java-EE-7 /JSR356 WebSocket-API).1. Erstellen eines WebSocket endpoint
Erstellen Sie zuerst eine
@ServerEndpoint
- Klasse, die im Grunde sammelt alle websocket-Sitzungen in einer Anwendung, die weit gesetzt. Beachten Sie, dass dies in diesem speziellen Beispiel nurstatic
wie jedes websocket-Sitzung grundsätzlich bekommt seine eigene@ServerEndpoint
Instanz (Sie sind im Gegensatz zu servlets somit staatenlos.Dem obigen Beispiel hat eine zusätzliche Methode
sendAll()
sendet die angegebene Nachricht an alle offenen websocket-sessions (d.h. application scoped-push). Beachten Sie, dass diese Meldung kann auch ganz gut einen JSON-string.Wenn Sie wollen, explizit speichern Sie Sie im Anwendungs-Bereich (oder (HTTP) session-scope), dann kann Ihnen der
ServletAwareConfig
Beispiel in diese Antwort. Sie wissen,ServletContext
Attribute anzeigen zuExternalContext#getApplicationMap()
im JSF - (undHttpSession
Attribute anzeigen zuExternalContext#getSessionMap()
).2. Öffnen Sie die WebSocket-client-Seite und hören Sie auf es
Verwenden Sie dieses Stück JavaScript zum öffnen einer websocket und hören Sie auf ihn:
Ab jetzt ist es lediglich die Anmeldung der eingeschobenen text. Wir möchten mit diesem text als eine Anleitung zum aktualisieren der menu-Komponente. Für dass, wir müssten eine zusätzliche
<p:remoteCommand>
.Vorstellen, dass Sie senden eine JS-Funktion den Namen als text
Push.sendAll("updateMenu")
dann könnte man interpretieren, und lösen Sie es wie folgt:Wieder, wenn über einen JSON-string als Nachricht (die könnte man analysieren, indem
$.parseJSON(event.data)
), mehr Dynamik möglich ist.3a. Entweder trigger WebSocket push von DB-Seite
Nun müssen wir den Befehl auslösen
Push.sendAll("updateMenu")
von der DB-Seite. Eine der einfachsten Möglichkeiten, es zu lassen die DB-Feuer eine HTTP-Anfrage an einen web service. Ein plain-vanilla-servlet ist mehr als ausreichend, um zu handeln wie ein web-service:Haben Sie natürlich die Möglichkeit zur Parametrisierung der push-Nachricht basiert auf request-Parameter-oder Pfad-info, wenn nötig. Vergessen Sie nicht, die Durchführung von Sicherheitsüberprüfungen, wenn der Anrufer darf auch aufrufen, servlet, sonst jemand in der Welt außer der DB selbst würde in der Lage sein, um es aufzurufen. Sie könnten überprüfen Sie die Anrufer-IP-Adresse, zum Beispiel, das ist praktisch, wenn beide DB-server und web-server laufen auf der gleichen Maschine.
Damit die DB das Feuer ein HTTP-request an das servlet, die Sie benötigen, erstellen Sie eine wiederverwendbare eine gespeicherte Prozedur, die im Grunde startet das Betriebssystem bestimmte Befehle ausführen eines HTTP GET-Anforderung, z.B.
curl
. MySQL nicht nativ unterstützt die Ausführung einer OS-spezifischen Befehl, so müssten Sie installieren eine benutzerdefinierte Funktion (UDF) für das erste. Bei mysqludf.org finden Sie eine Reihe von denen SYS ist von unserem Interesse. Es enthält diesys_exec()
Funktion, die wir brauchen. Wenn es installiert ist, erstellen Sie die folgende gespeicherte Prozedur in MySQL:Erstellen Sie jetzt insert/update/delete-Trigger wird aufgerufen (vorausgesetzt, die Tabelle Namen benannt ist
menu
):3b. Oder trigger WebSocket push von JPA-Seite
Wenn Ihre Anforderung/situation es erlaubt, zu hören auf der JPA-entity-change-events nur, und damit externe änderungen an der DB macht nicht behandelt werden müssen, dann können Sie statt DB-Trigger wie beschrieben in Schritt 3a auch nur mit einem JPA-entity-change-listener. Du kannst dich doch über
@EntityListeners
annotation auf der@Entity
Klasse:Wenn Sie geschehen, verwenden Sie eine einzelne web-Profil, Projekt, wobei alles (EJB/JPA/JSF) geworfen zusammen in das gleiche Projekt, dann kann man nur noch direkt aufrufen
Push.sendAll("updateMenu")
.Jedoch in "enterprise-Projekte" -, service-Ebene code (EJB/JPA/etc) ist in der Regel getrennt im EJB-Projekt, während web-layer code (JSF/Servlets/WebSocket/etc) ist gehalten, die in ein Web-Projekt. Die EJB-Projekt sollte keine einzige Abhängigkeit von web-Projekt. In diesem Fall würden Sie besser Feuer CDI
Event
statt, die das Web-Projekt könnte@Observes
.(Hinweis: die outcomments; die Injektion eines CDI -
Event
kaputt ist, sowohl GlassFish und WildFly in den aktuellen Versionen (4.1 /8.2); die Problemumgehung feuert das Ereignis überBeanManager
statt; wenn dies immer noch nicht funktioniert, die CDI-1.1 alternative istCDI.current().getBeanManager().fireEvent(new MenuChangeEvent(menu))
)Und dann im web-Projekt:
Update: zum 1. april 2016 (ein halbes Jahr nach der vorigen Antwort), OmniFaces eingeführt mit version 2.3 der
<o:Buchse>
sollte dies alles weniger umständlich. Die kommende JSF 2.3<f:websocket>
basiert weitgehend auf<o:socket>
. Siehe auch Wie kann man server-push-asynchrone änderungen an einer HTML-Seite erstellt von JSF?InformationsquelleAutor der Antwort BalusC
Da Sie mit Primefaces-und Java-EE-7 es sollte leicht zu implementieren:
verwenden, Primefaces Push ( Beispiel hier http://www.primefaces.org/showcase/push/notify.xhtml )
Hoffe, das hilft
Wenn Sie einige weitere details Fragen Sie einfach
Hinsichtlich
InformationsquelleAutor der Antwort urbiwanus
PrimeFaces hat Umfrage Funktionen zum aktualisieren der Komponente automatisch. Im folgenden Beispiel
<h:outputText>
wird automatisch aktualisiert, alle 3 Sekunden durch<p:poll>
.Erstellen einer listener-Methode wie
process()
wählen Sie Ihr Menü Daten.<p:poll>
wird automatisch aktualisieren Sie Ihre menu-Komponente.InformationsquelleAutor der Antwort CycDemo