Filas de consulte permitem que aplicativos criem uma fila qualificada por uma consulta no lado do servidor ou eXtreme Scale local sobre uma entidade. As entidades do resultado da consulta são armazenadas nesta fila. Atualmente, a fila de consulta é suportada apenas em um mapa que está utilizando a estratégia de bloqueio pessimista.
Uma fila de consulta é compartilhada por várias transações e clientes. Após a fila de consulta ficar vazia, a consulta da entidade associada a esta fila é executada novamente e novos resultados são incluídos na fila. Uma fila de consulta é identificada exclusivamente pela cadeia e os parâmetro de consulta da entidade. Há apenas uma instância para cada fila de consulta exclusiva em uma instância do ObjectGrid. Consulte a documentação da API do EntityManager para obter mais informações.
O exemplo a seguir mostra como a fila de consulta pode ser utilizada.
/**
* Get a unassigned question type task
*/
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();
}
O exemplo anterior primeiro cria um QueryQueue com uma cadeia de consulta de entidade, "SELECT t FROM Task t WHERE t.type=?1 AND t.status=?2". Depois ele configura os parâmetros para o objeto QueryQueue. Esta fila de consulta representa todas as tarefas "não-designadas" do tipo "questão". O objeto QueryQueue é muito semelhante a um objeto Query da entidade.
Após o QueryQueue ser criado, uma transação de entidade é iniciada e o método getNextEntity é chamado, que recupera a próxima entidade disponível sem um valor de tempo limite de 10 segundos. Após a entidade ser recuperada, ela é processada no método assignTask. O assignTask modifica a instância da entidade Task e altera o status para "designado" que efetivamente a remove da fila já que não corresponde mais ao filtro do QueryQueue. Uma vez designada, ocorre o commit da transação.
É possível recuperar entidades em um loop. A seguir está um exemplo que ilustra como obter todas as tarefas não-designadas do tipo question concluídas.
/**
* Get all unassigned question type tasks
*/
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);
}
Se houver 10 tarefas não-designadas do tipo question no mapa de entidade, é possível esperar que você terá 10 entidades impressas no console. Entretanto, se este exemplo for executado, visualizará que o programa nunca sai, o que pode ser contrário ao que você assumiu.
Quando uma fila de consulta é criada e o getNextEntity é chamado, a consulta de entidade associada com a fila é executada e os 10 resultados são colocadas na fila. Quando o getNextEntity é chamado, uma entidade é retirada da fila. Após 10 chamadas do getNextEntity serem executadas, a fila fica vazia. A consulta da entidade será novamente executada automaticamente. Como estas 10 entidades ainda existem e correspondem aos critérios de filtro da fila de consulta, elas são colocadas na fila novamente.
Se a linha a seguir for incluída após a instrução println(), você verá apenas 10 entidades impressas.
em.remove(nextTask);
Para obter informações sobre o uso de SessionHandle com QueryQueue em uma implementação de posicionamento por contêiner, leia sobre Integração de SessionHandle.
Em um eXtreme Scale distribuído, uma fila de consulta pode ser criada para uma partição ou todas as partições. Se uma fila de consulta é criada para todas as partições, haverá uma instância de fila de consulta em cada partição.
Quando um cliente tenta obter a próxima entidade utilizando o método QueryQueue.getNextEntity ou QueryQueue.getNextEntities, o cliente envia um pedido para uma das partições. Um cliente envia pedidos de peek e pin para o servidor:
A seguir, está um exemplo de como uma entidade é recuperada para uma fila de consulta que é implementada em todas as partições (n):
A seguir está o método para criar um objeto QueryQueue no entity manager:
public QueryQueue createQueryQueue(String qlString, Class entityClass);
O resultado na fila de consulta deve ser projetado para o objeto definido pelo segundo parâmetro para o método, Class entityClass.
Se este parâmetro for especificado, a classe deve ter o mesmo nome da entidade conforme especificado na cadeia de consultas. Isto é útil se você desejar projetar uma entidade em uma entidade de subconjunto. Se um valor nulo é utilizado com a classe de entidade, então, o resultado não será projetado. O valor armazenado no mapa estará em um formato de tupla da entidade.
No ambiente eXtreme Scale distribuído, a fila de consulta é suportada apenas para mapas do eXtreme Scale com o modo de bloqueio pessimista. Portanto, não há um cache local no lado do cliente. Entretanto, um cliente poderia ter dados (chave e valor) no mapa transacional. Isto potencialmente poderia levar a uma colisão de chaves quando uma entidade recuperada do servidor compartilha a mesma chave que uma entrada já no mapa da transação.
A colisão de chaves acontece apenas quando há dados no mapa transacional. Em outras palavras, isto ocorre apenas quando uma chamada getNextEntity ou getNextEntities é chamada em uma transação que já foi suja (um novo dado foi inserido ou um dado foi atualizado). Se um aplicativo não deseja que aconteça uma colisão de chaves, ele deve sempre chamar getNextEntity ou getNextEntities em uma transação que não foi suja.
No primeiro caso, o servidor descobre que o cliente está ficando inativo quando ele tenta enviar de volta a resposta para o cliente. No segundo caso, quando o cliente obtém uma ou mais entidades do servidor, um bloqueio X é colocado nestas entidades. Se o cliente fica inativo, a transação eventualmente atingirá o tempo limite e o bloqueio X será liberado.
Geralmente, as filas de consulta não honram a cláusula ORDER BY. Se você chamar getNextEntity ou getNextEntities a partir da fila de consulta, não há garantia de que as entidades serão retornadas de acordo com a ordem. O motivo é que as entidades não podem ser ordenadas entre partições. No caso em que a fila de consulta é implementada em todas as partições, quando uma chamada getNextEntity ou getNextEntities é executada, uma partição aleatória é selecionada para processar o pedido. Portanto, a ordem não é garantida.
ORDER BY será honrado se uma fila de consulta for implementada em uma partição única.
Para obter mais informações, consulte API de Consulta EntityManager.
Cada chamada QueryQueue.getNextEntity ou QueryQueue.getNextEntities recupera entidades correspondentes de uma partição aleatória. Os aplicativos devem chamar exatamente uma QueryQueue.getNextEntity ou QueryQueue.getNextEntities em uma transação. Caso contrário, o eXtreme Scale pode encerrar o toque a entidades de várias partições, fazendo com que uma exceção seja lançada na hora da confirmação.