Introducción a Bases de Datos Objeto Relacionales Oracle

En este artículo vamos a aprender a trabajar con Bases de Datos Objeto Relacionales Oracle. Como en la mayoría de los artículos publicados aquí sobre Oracle, voy a intentar ser lo más práctico posible y mostrar como podemos empezar a trabajar con objetos sin morir en el intento.

Creación de objetos

Los objetos están formados por:

  • Una especificación o interfaz pública que consta de la declaración de los atributos y, opcionalmente, de la definición de métodos.
  • Un cuerpo (parte privada) dónde se implementan los métodos definidos en la especificación. Si un objeto no tiene métodos definidos, no es necesario definir su cuerpo.
  • Especificación:
CREATE or REPLACE TYPE nombreObjeto AS OBJECT (
  atributo TIPO,
  ...,
  MEMBER FUNCTION nombreFuncion RETURN Tipo,
  MEMBER FUNCTION nombreFuncion2(nomVar TIPO) RETURN Tipo,
  MEMBER PROCEDURE nombreProcedimiento,
  PRAGMA RESTRICT_REFERENCES (nombreFuncion,restricciones),
  PRAGMA RESTRICT REFERENCES (nombreFuncion2,restricciones),
  PRAGMA RESTRICT_REFERENCES (nombreProcedimiento,restricciones)
);

Donde “restricciones” puede ser cualquiera de las siguientes o una combinación de ellas:

  • WNDS: Evita que el método pueda modificar las tablas de la BD.
  • RNDS: Evita que el método pueda leer las tablas de la base de datos.
  • WNPS: Evita que el método modifique variables del paquete PL/SQL.
  • RNPS: Evita que el método pueda leer las variables del paquete PL/SQL.
  • Cuerpo:
CREATE OR REPLACE TYPE BODY nombreObjeto AS 
  MEMBER FUNCTION nombreFuncion
  RETURN Tipo
  IS
    BEGIN
     ...
  END nombreFuncion; 
  MEMBER FUNCTION nombreFuncion2(nomVar TIPO)
  RETURN Tipo
  IS
    BEGIN
     ...
  END nombreFuncion2;
  MEMBER PROCEDURE nombreProcedimiento
  IS BEGIN
      ...
  END nombreProcedimiento;
END; /
  • Ejemplo de definición de objetos sin funciones ni cuerpo:
create or replace type direccion_obj as object(
   calle varchar2(20),
   cp varchar2(5),
   poblacion varchar2(20),
   provincia varchar2(20)
);
/ 
create or replace type persona_obj as object(
   idpersona number,
   dni varchar2(9),
   nombre varchar2(15),
   apellidos varchar2(30),
   fecha_nac date
);
/
  • Ejemplo de definición de objetos con funciones y cuerpo:
CREATE or REPLACE TYPE persona AS OBJECT ( 
   idpersona number,
   dni varchar2(9),
   nombre varchar2(15),
   apellidos varchar2(30),
   fecha_nac date,
   MEMBER FUNCTION muestraEdad RETURN NUMBER,
   PRAGMA RESTRICT_REFERENCES (muestraEdad,WNDS)
);
  • Cuerpo:
CREATE OR REPLACE TYPE BODY persona AS 
   MEMBER FUNCTION muestraEdad
   RETURN NUMBER
   IS
     BEGIN
      return to_char(sysdate, 'YYYY')-to_char(fecha_nac, 'YYYY');
   END muestraEdad; 
END;
/
  • Llamar a la función o procedimiento:

Para llamar a una función o procedimiento bastará con ejecutar aliasTabla.nombreFunción(parámetros) o aliasTabla.nombreProcedimiento(). Para ello, es necesario crear antes la tabla que contendrá al objeto tal y como se puede ver en el siguiente apartado.

select p.nombre, p.muestraEdad() from persona p where dni='123456789A';

ilumina2 photo

Creación de tablas con identidad de objeto

Para poder trabajar con objetos, es imprescindible crear una tabla con identidad de objeto o una tabla con columnas objeto (tabla sin identidad de objeto). Sobre un objeto no se pueden realizar acciones de insert, update, etc.

Las tablas con identidad de objeto se utilizan como si de una tabla normal se tratara dónde cada una de sus filas es una fila del objeto.

create table clientes of persona_obj(
  idpersona primary key
);
  • Inserción de registros
insert into clientes values(1, '123456789', 'aitor', 'rigada', '01/01/1900');

Creación de tablas sin identidad de objeto

 create table clientes(
   cliente persona_obj,
   direccion direccion_obj,
   fecha_alta date );
  • Inserción de registros
insert into clientes values( 
 persona_obj(1, '123456789', 'aitor', 'rigada', '01/01/1900'), 
 direccion_obj('Mimoses S/N', '08000', 'Sant Boi', 'Barcelona'), 
 sysdate);

Ejemplo de clave primaria (PRIMARY KEY) con tablas Objeto

create type persona_obj as object(
 idpersona number,
 nombre varchar2(20)
 );
  • Opción 1:
create table clientes of persona_obj(
 idpersona PRIMARY KEY
 );
  • Opción 2:
create table clientes2 of persona_obj(
CONSTRAINT pk_clientes2 PRIMARY KEY (idpersona)
);

Ejemplo de clave foránea (Foreign Key) con tablas objeto

create type persona_obj as object(
 idpersona number,
 nombre varchar2(20)
);
/
create table clientes of persona_obj(
 idpersona primary key
);
/
create or replace type producto_obj as object(
 idproducto number,
 nombre varchar2(20),
 ffabric date,
 idpersona number
);
/
create table productos of producto_obj(
 idproducto primary key,
 constraint fk_prod_pers foreign key (idpersona) references clientes(idpersona)
);
/
insert into persona values(1, 'aitor');
insert into productos values(1, 'D700', sysdate, 1);

PRIMARY KEY, FOREIGN KEY y CHECK con tablas sin identidad de objeto

Siguiendo el ejemplo anterior, modificaremos la creación de la tabla productos para que ésta no sea una tabla objeto. Una vez creada le añadiremos una clave primaria, una foránea y una restricción check a modo de ejemplo.

CREATE TABLE productos2(
 producto producto_obj,
 caducidad DATE,
 CONSTRAINT prod2_pk PRIMARY KEY (producto.idproducto),
 CONSTRAINT prod2_fk FOREIGN KEY (producto.idpersona) 
                                       REFERENCES clientes(idpersona),
 CONSTRAINT prod2_check_date CHECK (producto.nombre IS NOT NULL)
);

Ver tipos creados

select * from user_types

Tipos Colección: Creación de VARRAY

Conjunto de elementos ordenados y del mismo tipo. Debemos definir el número de posiciones que tendrá así como su tipo. No debemos sobrepasar el número de posiciones asignadas en su declaración.

CREATE TYPE nombreArray AS
VARRAY(NºPosiciones) OF Tipo;
/
  • Ejemplo:
CREATE TYPE lista_compra AS
VARRAY(20) OF VARCHAR2(10);
/
CREATE OR REPLACE TYPE compra_obj AS OBJECT(
   idcompra NUMBER,
   fecha DATE,
   lista lista_compra
);
/
CREATE TABLE compra OF compra_obj(
   idcompra PRIMARY KEY,
   fecha default SYSDATE
);

Actualización (UPDATE) de VARRAY

Las actualizaciones en los VARRAY deben ser atómicas, es decir, podemos modificar el VARRAY totalmente pero no parcialmente.

  • Ejemplo:
update compra set lista=lista_compra('item1', item2') where idcompra=42;

Tipos Colección: Creación de NESTED TABLES (Tablas anidadas)

CREATE OR REPLACE TYPE nombreTablaObjeto as OBJECT(
 atributo1 TIPO1,
 atributo2 TIPO2(n),
 ...
);
/
CREATE TYPE nombreColeccionObjeto
AS TABLE OF nombreTablaObjeto;
/
CREATE TABLE nombreTabla(
 atributo1 TIPO1
 atributo2 TIPO2,
 tablaAnidada nombreColeccionObjeto)
NESTED TABLE tablaAnidada STORE AS nombre_tabla;
  • Ejemplo:
CREATE OR REPLACE TYPE persona_obj AS OBJECT(
  nombre VARCHAR2(10),
  apellidos VARCHAR2(20),
  fnacimiento DATE
);
/
CREATE TYPE personaNT AS TABLE OF persona_obj;
/
CREATE TABLE empleados(
  idempleado NUMBER PRIMARY KEY,
  fecha_alta DATE DEFAULT SYSDATE,
  persona personaNT
) NESTED TABLE persona STORE AS personas_tab;

Insertar y seleccionar datos de una NESTED TABLE:

Siguiendo el ejemplo anterior de creación, podemos insertar datos y posteriormente seleccionarlos tal y como se muestra a continuación.

  • Inserción:
insert into empleados values(1, sysdate, personaNT(
  persona_obj2('Nombre1', 'Apellidos1', '07/05/1984'),
  persona_obj2('Nombre2', 'Apellidos2', '18/06/1983'),
  persona_obj2('Nombre3', 'Apellidos3', '01/10/1980'))
);
  • Selección:
select * from empleados;

Para trabajar con las tablas anidadas, debemos informar que el atributo persona es, en realidad, una tabla. De esta forma podremos trabajar con su contenido.

select e.fecha_alta, p.nombre from empleados e, table(e.persona) p;

Ejemplo completo de creación, inserción y actualización selectiva (parcial) de NESTED TABLE

En este ejemplo veremos como crear una tabla de usuarios con una tabla anidada en su interior para almacenar los contactos de estos usuarios. Una vez creada la tabla insertaremos algunos datos y veremos como podemos modificar el contenido de una nested table. Podremos observar como, a diferencia de lo que ocurre con los VARRAY, las tablas anidadas nos permiten hacer una actualización selectiva y no total.

Partimos de la base que existe previamente un objeto llamado persona_obj el cual contiene los atributos nombre (VARCHAR2), apellidos (VARCHAR2) y fnacimiento (DATE).

  • Creación de la tabla anidada:
create or replace type contactosTB as table of persona_obj;
  • Creación de la tabla usuarios con su tabla anidada como atributo:
create table usuarios(
  id number,
  nombre varchar2(10),
  contactos contactosTB
) nested table contactos store as contactos_TN;
  • Inserción de registros:
insert into usuarios values(1, 'Aitor', contactosTB(
   persona_obj('Toni', 'Gonzales', '05/8/1968'),
   persona_obj('Alex', 'Lugo', '07/05/1984'))
);
insert into usuarios values(2, 'Luisa', contactosTB(
   persona_obj('Jonathan', 'Garcia', '07/12/1987'))
);
insert into usuarios values(3, 'Hugo', contactosTB(
   persona_obj('Pepe', 'Ramos', '20/03/2005'),
   persona_obj('Han', 'Ruiz', '25/12/2004'),
   persona_obj('Lourdes', 'Sala', '01/01/1999'),
   persona_obj('Antonio', 'Lopez', '27/05/1988'))
);
  • Mostramos los datos para comprobar que la inserción ha sido correcta:
select us.id, us.nombre, cn.nombre from ustest us, table(us.contactos) cn;
  • Actualizamos la tabla anidada

Modificaremos el usuario con id=3. Cambiaremos el nombre del contacto que se apellida “Ramos” a “Luisa”.

update table(select us.contactos from usuarios us where us.id=3)
   set nombre='Luisa' where apellidos='Ramos';

Valores por defecto (DEFAULT) en NESTED TABLE y VARRAY

Se pueden definir valores por defecto en una columna de objeto tal y como hemos visto. También es posible realizar esta acción sobre una columna de tabla anidada o sobre un array.

Sobre el ejemplo anterior quedaría de la siguiente manera.

CREATE TABLE empleados(
  idempleado NUMBER PRIMARY KEY,
  fecha_alta DATE DEFAULT SYSDATE,
  persona personaNT DEFAULT personaNT(
     persona_obj('Nombre1', 'Apellidos1', '07/05/1984'))
)NESTED TABLE persona STORE AS personas_tab;

Incluso podríamos añadir como valor por defecto, a más de una persona si las separamos mediante comas:

CREATE TABLE empleados(
  ...,
  persona personaNT DEFAULT personaNT(
    persona_obj('Nombre1', 'Apellidos1', '07/05/1984'),
    persona_obj('Nombre2', 'Apellidos2', '18/06/1983'))
)NESTED TABLE persona STORE AS personas_tab;

Atributos de NESTED TABLE no nulos

Para declarar como NO NULO un atributo de un objeto contenido en una tabla anidada debemos modificar la tabla generada dónde se van a guardar los datos. Esto no podemos hacerlo en el momento de la creación de la tabla sino que debemos hacerlo una vez se ha creado.

ALTER TABLE tabla_generada ADD CONSTRAINT nombre_constraint CHECK(atributo is not null);
  • Ejemplo:
CREATE TABLE empleados(
  ...,
  persona personaNT
)NESTED TABLE persona STORE AS personas_tab;

ALTER TABLE personas_tab ADD CONSTRAINT nn_personas_tab_apellido CHECK(apellido IS NOT NULL);

Herencia

Para permitir la herencia entre objetos debemos especificar la opción NOT FINAL en la creación del objeto para que otros objetos puedan heredar de él (valor por defecto). Si queremos que de un objeto no se pueda heredar, debemos especificar FINAL. Lo mismo podemos hacer para los procedimientos o funciones miembro.

CREATE TYPE nombreSuperObj AS OBJECT(
    nombreAtr TIPO,
    ... ,
    FINAL MEMBER FUNCTION nombreFuncion1 RETURN NUMBER,
    MEMBER FUNCTION nombreFuncion2 RETURN VARCHAR2
) NOT FINAL;

Para especificar que un objeto heredará de un objeto padre, se debe especificar la clausula UNDER. Si además queremos sobrescribir un método  debemos añadir la clausula OVERRIDING antes de MEMBER (tanto en la especificación como en el cuerpo del objeto) tal y como podemos ver a continuación.

CREATE TYPE nombreSubObj UNDER nombreSuperObj(
    nombreAtr TIPO,
    ... ,
    OVERRIDING MEMBER FUNCTION nombreFuncion2 RETURN VARCHAR2
) FINAL;
  • Ejemplo
CREATE TYPE empleado_obj AS OBJECT(
    id_empl NUMBER,
    sueldo_base NUMBER,
    comision NUMBER,
    nombre VARCHAR2(10),
    apellidos VARCHAR2(25),
    MEMBER FUNCTION calc_sueldo_final RETURN NUMBER
) NOT FINAL;
/
CREATE OR REPLACE TYPE BODY empleado_obj AS
    MEMBER FUNCTION calc_sueldo_final
    RETURN NUMBER
    IS
    BEGIN
        RETURN sueldo_base+sueldo_base*comision;
    END calc_sueldo_final;
END;
/
CREATE TYPE ingeniero_obj UNDER empleado_obj(
    num_proyectos NUMBER,
    fecha_titulacion DATE
) FINAL;
/
CREATE TYPE arquitecto_obj UNDER empleado_obj(
    extras NUMBER,
    obras_realizadas NUMBER,
    OVERRIDING MEMBER FUNCTION calc_sueldo_final RETURN NUMBER
) FINAL;
/
CREATE OR REPLACE TYPE BODY arquitecto_obj AS
    OVERRIDING MEMBER FUNCTION calc_sueldo_final
    RETURN NUMBER
    IS
    BEGIN
        RETURN sueldo_base+sueldo_base*comision+extras;
    END calc_sueldo_final;
END;
/
CREATE TABLE empleados OF empleado_obj;
CREATE TABLE ingenieros OF ingeniero_obj; 
CREATE TABLE arquitectos OF arquitecto_obj;
  • Ejemplo de inserción de registros en objeto heredado
--INSERT INTO arquitectos VALUES(id_empl, sueldo_base, comision, nombre, apellidos, extras, obras_realizadas);
INSERT INTO arquitectos VALUES(1, 1500, 0.2, 'Nombre1', 'Apellidos1', 500, 12);
--INSERT INTO ingenieros VALUES(id_empl, sueldo_base, comision, nombre, apellidos, num_proyectos, fecha_titulacion);
INSERT INTO ingenieros VALUES(2, 1500, 0.2, 'Nombre2', 'Apellidos2', 3, '07/09/2008');
--INSERT INTO empleados VALUES(id_empl, sueldo_base, comision, nombre, apellidos);
INSERT INTO empleados VALUES(3, 1500, 0.2, 'Nombre3', 'Apellidos3'); 

select e.nombre, e.sueldo_base, e.calc_sueldo_final() from empleados e;
select i.nombre, i.sueldo_base, i.calc_sueldo_final() from ingenieros i;
select a.nombre, a.sueldo_base, a.calc_sueldo_final() from arquitectos a;

Podremos observar como, al llamar a la función calc_sueldo_final tanto de empleados como de ingenieros se ejecuta la función de la super clase ya que ingenieros no la reescribe. En cambio, al llamar a la función calc_sueldo_final de arquitectos, se ejecuta la definida en el cuerpo de arquitectos.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: