Abfragewarteschlangen ermöglichen Anwendungen, eine durch Abfrage im serverseitigen oder lokalen eXtreme Scale über eine Entität qualifizierte Warteschlange zu erstellen. Entitäten aus dem Abfrageergebnis werden in dieser Warteschlange gespeichert. Derzeit werden Abfragewarteschlangen nur in Maps unterstützt, die die pessimistische Sperrstrategie verwenden.
Eine Abfragewarteschlange wird von mehreren Transaktionen und Clients gemeinsam genutzt. Wenn die Abfragewarteschlange leer ist, wird die Entitätenabfrage, die dieser Warteschlange zugeordnet ist, erneut ausgeführt, und die neuen Ergebnisse werden der Warteschlange hinzugefügt. Eine Abfragewarteschlange wird über die Entitätsabfragezeichenfolge und -parameter eindeutig identifiziert. Es gibt nur eine einzige Instanz jeder eindeutigen Abfragewarteschlange in einer ObjektGrid-Instanz. Weitere Informationen finden Sie in der API-Dokumentation zu EntityManager.
Das folgende Beispiel veranschaulicht, wie eine Abfragewarteschlange verwendet werden kann.
/**
* Nicht zugordnete Task vom Typ "Frage" abrufen
*/
private void getUnassignedQuestionTask() throws Exception {
EntityManager em = og.getSession().getEntityManager();
EntityTransaction tran = em.getTransaction();
QueryQueue queue = em.createQueryQueue("SELECT t FROM Task t
WHERE t.type=?1 AND t.status=?2", Task.class);
queue.setParameter(1, new Integer(Task.TYPE_QUESTION));
queue.setParameter(2, new Integer(Task.STATUS_UNASSIGNED));
tran.begin();
Task nextTask = (Task) queue.getNextEntity(10000);
System.out.println("next task is " + nextTask);
if (nextTask != null) {
assignTask(em, nextTask);
}
tran.commit();
}
Der vorherige Beispielcode erstellt zunächst eine Abfragewarteschlange (QueryQueue) mit der Entitätsabfragezeichenfolge "SELECT t FROM Task t WHERE t.type=?1 AND t.status=?2". Anschließend setzt er die Parameter des QueryQueue-Objekts. Diese Abfragewarteschlange stellt alle "nicht zugeordneten" Tasks des Typs "Frage" dar. Das QueryQueue-Objekt ist dem Query-Objekt einer Entität sehr ähnlich.
Nach dem Erstellen des QueryQueue-Objekts wird eine Entitätstransaktion gestartet und die Methode "getNextEntity" aufgerufen, die die nächste verfügbare Entität mit einem definierten Zeitlimit von 10 Sekunden aufruft. Nachdem die Entität abgerufen wurde, wird sie in der Methode "assignTask" verarbeitet. Die Methode "assignTask" modifiziert die Taskentitätsinstanz und ändert den Status in "zugeordnet"(assigned), woraufhin die Instanz aus der Warteschlange entfernt wird, weil sie dem Filter von QueryQueue nicht mehr entspricht. Nach der Zuordnung wird die Transaktion festgeschrieben.
Sie können Entitäten in einer Schleife abrufen. Im Folgenden sehen Sie ein Beispiel, das veranschaulicht, wie alle nicht zugeordneten Tasks vom Typ "Frage" abgerufen werden:
/**
* Alle nicht zugeordneten Tasks vom Typ "Frage" abrufen
*/
private void getAllUnassignedQuestionTask() throws Exception {
EntityManager em = og.getSession().getEntityManager();
EntityTransaction tran = em.getTransaction();
QueryQueue queue = em.createQueryQueue("SELECT t FROM Task t WHERE
t.type=?1 AND t.status=?2", Task.class);
queue.setParameter(1, new Integer(Task.TYPE_QUESTION));
queue.setParameter(2, new Integer(Task.STATUS_UNASSIGNED));
Task nextTask = null;
do {
tran.begin();
nextTask = (Task) queue.getNextEntity(10000);
if (nextTask != null) {
System.out.println("next task is " + nextTask);
}
tran.commit();
} while (nextTask != null);
}
Wenn es 10 nicht zugeordnete Tasks vom Typ "Frage" in der Entitäts-Map gibt, erwarten Sie wahrscheinlich, dass 10 Entitäten in der Konsole ausgegeben werden. Wenn Sie jedoch dieses Beispiel ausführen, werden Sie feststellen, dass das Programm entgegen Ihren Erwartungen nie beendet wird.
Wenn eine Abfragewarteschlange erstellt und die Methode "getNextEntity" aufgerufen wird, wird die Entitätsabfrage ausgeführt, die der Warteschlange zugeordnet ist, und die 10 Ergebnisse werden in die Warteschlange eingetragen. Beim Aufruf von "getNextEntity" wird der Warteschlange eine Entität entnommen. Nach der Ausführung von zehn getNextEntity-Aufrufen ist die Warteschlange leer. Die Entitätsabfrage wird automatisch erneut ausgeführt. Da diese 10 Entitäten immer noch vorhanden sind und den Filterkriterien der Abfragewarteschlange entsprechen, werden sie erneut in die Warteschlange eingetragen.
Wenn die folgende Zeile hinter der Anweisung "println()" hinzugefügt wird, werden nur 10 Entitäten ausgegeben:
em.remove(nextTask);
Informationen zur Verwendung von SessionHandle mit QueryQueue in einer containerbezogenen Verteilungsimplementierung finden Sie unter SessionHandle-Integration.
In einer verteilten eXtreme-Scale-Umgebung kann eine Abfragewarteschlange für eine einzige oder für alle Partitionen erstellt werden. Wenn eine Abfragewarteschlange für alle Partitionen erstellt wird, gibt es in jeder Partition eine einzige Instanz der Abfragewarteschlange.
Wenn ein Client versucht, die nächste Entität mit der Methode QueryQueue.getNextEntity oder QueryQueue.getNextEntities abzurufen, sendet der Client eine Anforderung an eine der Partitionen. Ein Client sendet so genannte Peek- und Pin-Anforderungen an den Server:
Im Folgenden sehen Sie ein Beispiel, in dem veranschaulicht wird, wie eine Entität für eine Abfragewarteschlange abgerufen wird, die in allen Partitionen (n) implementiert ist:
Im Folgenden wird die Methode zum Erstellen eines QueryQueue-Objekt im EntityManager veranschaulicht:
public QueryQueue createQueryQueue(String qlString, Class entityClass);
Das Ergebnis in der Abfragewarteschlange muss an das Objekt weitergegeben werden, das mit dem zweiten Parameter der Methode definiert ist, Class entityClass.
Wenn dieser Parameter angegeben ist, muss die Klasse denselben Entitätsnamen haben, der auch in der Abfragezeichenfolge enthalten ist. Dies ist hilfreich, wenn Sie eine Entität an eine Teilentität weitergeben möchten. Wird ein Nullwert als Entitätsklasse verwendet, wird das Ergebnis nicht weitergegeben. Der in der Map gespeicherte Wert hat das Entitätstupelformat.
In einer verteilten eXtreme-Scale-Umgebung werden Abfragewarteschlangen nur für eXtreme-Scale-Maps mit pessimistischem Sperrmodus unterstützt. Deshalb gibt es auf der Clientseite keinen nahen Cache. Ein Client kann jedoch Daten (Schlüssel und Wert) in einer Transaktions-Map verwalten. Dies könnte zu einer Schlüsselkollision führen, wenn eine vom Server abgerufene Entität denselben Schlüssel wie ein Eintrag verwendet, der bereits in der Transaktions-Map enthalten ist.
Die Schlüsselkollision kommt nur vor, wenn die Transaktions-Map Daten enthält. Anders ausgedrückt, es kommt nur dann zu einer Schlüsselkollision, wenn ein Aufruf der Methode "getNextEntity" oder "getNextEntities" in einer Transaktion vorgenommen wird, die bereits genutzt wurde (es wurden neue Daten eingefügt oder aktualisiert). Wenn eine Anwendung Schlüsselkollisionen vermeiden möchten, muss sie getNextEntity oder getNextEntities immer in einer noch nicht genutzten Transaktion aufrufen.
Im ersten Fall erkennt der Server, dass der Client nicht mehr verfügbar ist, wenn er versucht, die Antwort an den Client zurückzusenden. Im zweiten Fall wird eine X-Sperre für diese Entitäten gesetzt. Wenn der Client abstürzt, läuft irgendwann das Transaktionszeitlimit ab, und die X-Sperre wird freigegeben.
Im Allgemeinen wird die Klausel ORDER BY von Abfragewarteschlangen nicht berücksichtigt. Wenn Sie getNextEntity oder getNextEntities über die Abfragewarteschlange aufrufen, besteht keine Garantie, dass die Entitäten in der richtigen Reihenfolge zurückgegeben werden. Der Grund hierfür ist, dass die Entitäten in den Partitionen nicht sortiert werden können. Wenn die Abfragewarteschlange in allen Partitionen implementiert ist und ein getNextEntity- oder getNextEntities-Abruf ausgeführt wird, wird eine Partition für die Verarbeitung der Anforderung zufällig ausgewählt. Deshalb ist die Reihenfolge nicht garantiert.
ORDER BY wird berücksichtigt, wenn eine Abfragewarteschlange nur in einer einzigen Partition implementiert ist.
Weitere Informationen finden Sie im Abschnitt EntityManager-API "Query".
Jeder Aufruf von QueryQueue.getNextEntity oder QueryQueue.getNextEntities ruft übereinstimmende Entitäten aus einer wahlfreien Partition ab. Anwendungen müssen einen einzigen Aufruf von QueryQueue.getNextEntity oder QueryQueue.getNextEntities in einer Transaktion absetzen. Andernfalls könnte eXtreme Scale Entitäten aus mehreren Partitionen abrufen, was dazu führt, dass beim Festschreiben eine Ausnahme ausgelöst wird.