Mi primera cola
Creacion de una cola
Para crear una cola, se deben seguir los siguientes pasos:
- Crear el tipo de datos que se precise para el cuerpo de la cola.
- Crear la queue table que es la base de la persistencia y transaccionalidad de la cola
- Crear la cola en sí
Vamos a ver cada uno de estos pasos
Creacion del tipo de datos
Una cola se utiliza para encolar y guardar items. Antes de crear la cola, debemos indicar que información y estructura deben tener estos items.
En la BBDD Oracle, esto implica crear un TYPE en la BBDD. TYPE es una estructura SQL (no confundir con las estructuras de PL/SQL). Podemos ver un documento más amplio al respecto aqui.
En resumen, podemos decir que se puede crear un objeto tan complejo como se desee comno composición de otros tipos.
Para que un usuario pueda crear tipos, debe tener el GRANT de CREATE TYPE. Este type se crea en su esquema.
Como ejemplo, vamos a ver como se crea un objeto "Cabecera y lineas" para contener un item factura, albaran o similar:
-- primero se crea una linea: CREATE TYPE linea AS OBJECT ( cantidad NUMBER(6), descripcion VARCHAR2(100), precio NUMBER) / show errors -- Ahora creamos un array de lineas CREATE TYPE lineas AS VARRAY(20) OF linea / show errors -- Finalmente la estructura completa CREATE TYPE factura AS OBJECT ( num_factura NUMBER, fecha DATE, observaciones VARCHAR2(100), cliente VARCHAR2(20), productos lineas) / show errors -- y para ver los tipos creados desc linea desc lineas desc factura
Creación de la Queue Table
Antes de crear una cola, debemos crear una Queue Table. Dependiendo del tipo de cola, se van a crear diversas tablas y vistas: estas tablas y vistas solo permiten acceso en lectura (SELECT) y no se pueden modificar directamente con sentencia SQL. Se modifican a través de la API de las colas.
Para poder crear una queue table, el usuario debe tener permiso de ejecución en el package DBMS_AQADM. La queue table se crea en su esquema.
La creación de una Queue Tables es tan simple como ejecutar:
BEGIN dbms_aqadm.create_queue_table ( queue_table=> 't_factura', -- nombre de la Queue table queue_payload_type=> 'factura', -- Tipo de datos que soporta entre comillas multiple_consumers=> FALSE, -- si es multiple consumers message_grouping=> DBMS_AQADM.NONE -- si agrupa los mensajes por transacciones ); COMMIT; --> no olvidar el commit END; /
La opción multiple_consumers se refiere a si un mensaje puede ir destinado a más de un destinatario. En particular, no se refiere a si una cola puede tener varios procesos desencolando de la misma.
Empezamos con una cola No Multiple_consumers porque son las más habituales y es más sencillo trabajar con ellas.
La opción message_grouping indica si los mensajes se deben agrupar por transacciones de la BBDD o bien cada mensaje es independiente.
Creación de una cola
Para poder crear una cola, el usuario debe tener permiso de ejecución en el package DBMS_AQADM. La cola se crea en su esquema.
La creación de una cola es tan simple como ejecutar:
BEGIN dbms_aqadm.create_queue( queue_name=> 'c_factura', -- Nombre de la cola queue_table=> 't_factura', -- Queue table en la que se crea queue_type=> DBMS_AQADM.NORMAL_QUEUE -- Hay colas normales y de error ); COMMIT; END; / BEGIN dbms_aqadm.start_queue('c_factura', TRUE, TRUE); -- permitir encolar y desencolar COMMIT; END; /
Por defecto, al crear una cola, no se permite encolar y desencolar. El procedimiento start_queue se ejecuta para que se puedan encolar y desencolar mensajes de la cola.
Con estos procesos ya tenemos creada una cola.
Las colas se identifican por su schema.nombre
En versión 11 se deben dar permisos para encolar y desencolar a los usuarios o roles que se desee.
BEGIN
DBMS_AQADM.GRANT_QUEUE_PRIVILEGE (
'ALL', -- privilege IN VARCHAR2,
'c_factura', -- queue_name IN VARCHAR2,
'usuario_o_rol', -- grantee IN VARCHAR2,
FALSE); -- grant_option IN BOOLEAN := FALSE);
END;
/
Se pueden crear varias colas sobre la misma queue table sin que ello implique pérdida de rendimiento en los accesos. Por supuesto, estas colas tienen el mismo tipo de datos (ya que el tipo de datos se define al crear la queue table). Si deseamos cols con distinto tipo de datos, se deben crear sobre distintas queue tables
Encolar y Desencolar
Vamos a ver 2 procedimientos sencillos para encolar y desencolar mensajes de una cola.
Cada vez que se llama al procedimiento encolar, se añade un mensaje a la cola.
Cada vez que se llama al procedimeinto desencolar, se obtiene el primer mensaje de la cola (y se desencola). Si no hay mensajes en la cola, el procedimiento se espera hasta que haya alguno.
Encolar
El procedimiento de encolar es tan sencillo como:
CREATE OR REPLACE PROCEDURE ENCOLA (datos in factura, cola in VARCHAR2) as enq_opt dbms_aq.enqueue_options_t; msg_opt dbms_aq.message_properties_t; msg_id raw(16); begin DBMS_AQ.ENQUEUE (cola, --queue_name IN VARCHAR2 enq_opt, --enqueue_options IN , msg_opt, --message_properties IN message_properties_t, datos, --payload msg_id); --msgid OUT RAW);; end; / show errors
Para crear este procedimeinto se debe tener el GRANT de CREATE PROCEDURE y permiso de ejecución sobre DBMS_AQ
Obsérvese que las variables del procedimeinto (enq_opt, msg_opt y msg_id) son obligatorias, pero tomamos su valores por defecto. msg_id devuelve el identificador interno del mensaje. Existe el campo correlation_id que se puede utilizar como identificador del mensaje definido por el usuario (veremos ejemplos más adelante).
Los parámetros del procedimiento son la cola donde queremos poner los datos y el dato en sí.
Este procedimiento no hace COMMIT: el mensaje encolado estará disponible cuando la transacción haga COMMIT. Si la transacción hace ROLLBACK, el mensaje desaparece de la cola: se hace rollback del enqueue.
Por supuesto, una misma transacción puede encolar varios mensajes en la misma cola o en distintas colas y distintas sesiones pueden encolar en la misma cola.
No se permite encolar en colas remotas: la cola debe ser siempre local (estar en la propia Base de Datos)
Desencolar
Para desencolar el primer mensaje de la cola, podemos crear un procedimeinto del tipo:
CREATE OR REPLACE PROCEDURE DESENCOLA (datos out factura, cola in VARCHAR2) as
deq_opt dbms_aq.dequeue_options_t;
msg_opt dbms_aq.message_properties_t;
msg_id raw(16);
begin
DBMS_AQ.DEQUEUE (cola, --queue_name IN VARCHAR2,
deq_opt, --dequeue_options IN ,
msg_opt, --message_properties IN message_properties_t,
datos, --OUT payload
msg_id ); --msgid OUT RAW);;
end;
/
show errors
Para crear este procedimeinto se debe tener el GRANT de CREATE PROCEDURE y permiso de ejecución sobre DBMS_AQ
Obsérvese que las variables del procedimeinto (deq_opt, msg_opt y msg_id) son obligatorias, pero tomamos su valores por defecto. msg_id devuelve el identificador interno del mensaje.
Los parámetros del procedimiento son la cola de donde queremos obtener los datos y el dato en sí.
Este procedimiento no hace COMMIT: el mensaje desencolado se eliminará de la cola cuando la transacción haga COMMIT. Si la transacción hace ROLLBACK, el mensaje vuelve a la cola: se hace rollback del dequeue.
Por supuesto, una misma transacción puede encolar y desencolar varios mensajes en la misma cola o en distintas colas y distintas sesiones pueden desencolar en la misma cola al mismo tiempo: en este caso, cada sesión obtendrá mensajes distintos.
No se permite desencolar de colas remotas: la cola debe ser siempre local (estar en la propia Base de Datos).
Ver el contenido de una cola
Al crear la queue table y las colas, se crean una seri de tablas y vistas que podemos consultar (pero no modificar su contenido). Todas ellas se forman a prtir del nombre de la queue table.
Mensajes que hay en una cola
Podemos ver los mensajes que hay en una cola consultando la vista AQ$<queuetable>
Ejemplo:
SQL> select queue, msg_id, to_char(enq_time,'yyyy-mm-dd hh24:mi:ss') enq_time, msg_state, enq_txn_id
from aq$t_factura;
QUEUE MSG_ID ENQ_TIME MSG_STATE ENQ_TXN_ID
----------- --------------------------------- -------------------- ---------- -----------
C_FACTURA 73ECB6703FE60382E040007F01017DFB 2009-09-19 23:10:37 READY 6.42.4624
Se muestra la cola, el MSG_ID o identificador interno del mensaje (es distinto para cada mensaje), la hora de encolar, el estado del mensaje (READY indica que está listo para desencolar) y la transacción que lo ha encolado.
Ver el contenido de un mensaje
Utilizando sqlplus, se puede ver el contenido del mensje. Para ello, basta conmostrar el campo user_date de la tabla <queuetable>.
Vamos a ver un ejemplo:
SQL> select user_data
from t_factura
where msgid = '73ECB6703FE60382E040007F01017DFB';
USER_DATA(NUM_FACTURA, FECHA, OBSERVACIONES, CLIENTE, PRODUCTOS(CANTIDAD, DESCRIPCION, PRECIO))
-----------------------------------------------------------------------------------------------
FACTURA(39, '19-SEP-09', 'observaciones factura', 'cliente 24', LINEAS(LINEA(2, 'desc1', 25), L
INEA(4, 'desc2', NULL), LINEA(1, 'desc3', 1)))
Se nos muestra el contenido en formato de objeto y según sus constructores.
Es de destacar que sqlplus es uno de los pocos programas que permiten visualizar el contenido de los datos de forma directa.
Sesión práctica de encolar y desencolar
Vamos aver una sesión de ejemplo práctico de como se encola y desencola.
Crearemos un procedimiento auxiliar que cree mensajes y los encole. Asi, además, vemos ejemplos de como trabajar con objetos:
CREATE OR REPLACE PROCEDURE ENCOLAR_MENSAJE (num in NUMBER, cola in VARCHAR2 default 'c_factura') as v_datos factura; v_lineas lineas; BEGIN v_lineas := lineas(); -- constructor: array vacio -- antes de llenar el array de lineas, hay que dimensionarlo v_lineas.extend(3); -- el array ahora tiene 3 elementos -- llenamos las lineas con valores. -- Observese el constructor linea(cantidad, descripcion, precio) v_lineas(1) := linea(2, 'desc1', 25); v_lineas(2) := linea(4, 'desc2', NULL); v_lineas(3) := linea(1, 'desc3', 1); -- Ahora la factura: v_datos := factura( num, sysdate, 'observaciones factura', 'cliente 24', v_lineas); -- y encolamos encola(v_datos, cola); end; / show errors
Para este ejemplo suponemos:
- Un usuario de BBDD ha ejecutado las sentencias anteriores para crear las colas.
- Hay dos sesiones de sqlplus con el mismo usuario que ha creado las colas.
| Sesion 1 | Sesion 2 |
|---|---|
SQL> --Encolamos dos mensajes SQL> exec encolar_mensaje(1); SQL> exec encolar_mensaje(2); |
|
SQL> set serveroutput on
SQL> -- Los desencolamos:
SQL> declare
datos factura;
begin
desencola(datos, 'c_factura');
dbms_output.put_line(datos.num_factura);
end;
/
... y no pasa nada: la rutina no termina Esto es debido a que en la sesion 1 no hemos hecho COMMIT; |
|
SQL> COMMIT; |
|
1 SQL> -- despues del commit, desencola ha terminado SQL> -- se ha desencolado la factura num 1 SQL> ROLLBACK; Al hacer ROLLBACK, el elemento vuelve a la cola. |
|
SQL> set serveroutput on
SQL> declare
datos factura;
begin
desencola(datos, 'c_factura');
dbms_output.put_line(datos.num_factura);
end;
/
1
SQL>
Se ha desencolado otra vez la factura num 1 |
|
SQL> declare
datos factura;
begin
desencola(datos, 'c_factura');
dbms_output.put_line(datos.num_factura);
end;
/
2
SQL>
En esta sesion, hemos desencolado otro elemento: la factura num 2. Obsérvese que esta sesión ha desencolado otro elemento a pesar que la factura num 1 está pendiente de COMMIT/ROLLBACK. Este comportamiento es difícil de conseguir con SQL. |
|
SQL> declare
datos factura;
begin
desencola(datos, 'c_factura');
dbms_output.put_line(datos.num_factura);
end;
/
Esta sesion se queda parada porque no hay mas elementos en la cola |
|
SQL> ROLLBACK; Devolvemos la factura num 1 a la cola |
|
1 SQL> COMMIT; La rutina desencola termina y se devuelve la factura num 1 Finalmente hacemos COMMIT para vaciar la cola definitivamente. |
