SGBDOO. Objetos en Oracle

Presentación de la Universidad Rey Juan Carlos
Teoria de BR Objeto Relacionales

Definición de un objeto

Para definir un objeto se crea un nuevo tipo.
Se crea un constructor por defecto que debe recibir los atributos en el orden en el que se hayan definido al crear el nuevo tipo

A) Creamos un objeto del tipo TABLON con 2 atributos (largo y ancho) y 3 métodos (area, perimetro y precio al que se le pasará un precio por metro cuadrado):

create or replace TYPE TABLON AS OBJECT (
  largo integer,
  ancho integer,
  member function area return integer,
  member function perimetro return integer,
  member function precio (p float) return float
);

B) Definimos el cuerpo del objeto (la definición de los métodos)

create or replace TYPE BODY TABLON AS

  member function area return integer AS
    resultado integer;
  BEGIN
    resultado:=largo*ancho;
    RETURN resultado;
  END area;

  member function perimetro return integer AS
    resultado integer;
  BEGIN
    resultado:=2*largo+2*ancho;
    RETURN resultado;
  END perimetro;

  member function precio (p float) return float AS
    resultado float;
  BEGIN
    resultado:=area*p;
    RETURN resultado;
  END precio;

END;

Uso de sentencias SQL con objetos:

Creación de tablas en las que uno de los campos es un objeto. 

Crearemos una tabla llamada mueble que contiene objetos del tipo TABLON (ver dibujo) :

CREATE TABLE MUEBLE ( NUMERO INT, CANTIDAD INT, ELEMENTO CHEMA.TABLON);

Inserción de registros en la tabla MUEBLE

Creamos los registros del mueble:

insert into mueble values(1,2,new tablon(180,30));
insert into mueble values(2,3,new tablon(100,30));
insert into mueble values(3,1,new tablon(120,30));

Operaciones sobre el mueble:

Ver los componentes: select * from mueble m;



Dimensiones de las tablas: select m.cantidad,m.elemento.largo,m.elemento.ancho from mueble m;



Areas, perímetros y coste de cada tabla (a 23 €/m2): select m.cantidad,m.elemento.area(),m.elemento.perimetro(),m.elemento.precio(0.0023) from mueble m;



Precio total del mueble (a 23 €/m2): select sum(m.cantidad*m.elemento.precio(0.0023)) from mueble m; ------> 53,82 €

Uso de objetos en funciones

create or replace FUNCTION funcion_con_objeto(a int,b int) RETURN VARCHAR2 AS
  r1 tablon;
BEGIN
  r1 := NEW tablon(a,b);
  return 'El area es '||r1.area();
END;

select funcion_con_objeto(10,20) from dual; devuelve el texto ‘El area es 200’


Atributos multivaluados

La 1FN indica que no deben asistir atributos multivaluados. Sin embargo, en BDOO se puede crear atributos que puedan tener más de un valor y ser manejados de una forma sencilla

Colecciones

Es un grupo de elementos del mismo tipo. Por ejemplo, supongamos que se desea almacenar datos atómicos de los empleados de una empresa (por ejemplo, el nombre) junto con el nombre de sus hijos (multivaluado). En BD relacionales se generan 2 tablas: empleados e hijos. La FK de hijos es la PK de empleados. En BD OO se crearía una colección:
Tipos - Nuevo Tipo - Nombre=colec_hijos - Tipo=tipo de matriz - Definir - Compilar
CREATE OR REPLACE TYPE COLEC_HIJOS AS VARRAY(10) OF varchar2(30) ;

De esta forma, se define el tipo colec_hijos que puede almacenar un máximo de 10 hijos, cada uno con hasta 30 caracteres en su nombre

Una vez definida la colección, se puede crear la tabla empleado que contiene un campo del tipo colecc_hijos
CREATE TABLE "CHEMA"."EMPLEADO" (
    "ID"        NUMBER,
    "NOMBRE"    VARCHAR2(20 BYTE),
    "APELLIDOS" VARCHAR2(20 BYTE),
    "HIJOS" "CHEMA"."COLEC_HIJOS" ,
    CONSTRAINT "PRK1" PRIMARY KEY ("ID")
)

A continuación se pueden insertar registros en la tabla empleado y seleccionarlos:


No es posible acceder a los elementos individuales de una colección desde un comando SQL. Es preciso realizar un programa PL-SQL

Tablas anidadas

Existe la posibilidad de no limitar la cantidad de valores dentro del atributo multivaluado, haciendo que el campo sea del tipo tabla

En el ejemplo anterior, el número máximo de hijos era de 10. Podemos hacerlo ilimitado definiendo un tipo tabla:
CREATE OR REPLACE TYPE TABLA_HIJOS AS TABLE OF varchar2(30);

Después se crea la tabla empleado con un campo del tipo tabla_hijos. Es preciso desde el editor de la tabla dar un nombre a la tabla anidada -nested table- (en nuestro caso t_hijos)
De esta forma se pasa de tablas de 2 dimensiones (fila, columna) a 3 dimensiones (fila, columna, fila de tipos). Evidentemente se puede tener varios niveles de tablas anidadas

Ver el gráfico con las distintas órdenes para insertar, seleccionar, borrar, sustituir, ...



Lo más útil es
  1. Crear un tipo objeto con sus atributos
  2. Crear un tipo tabla con el objeto anterior
  3. Crear una tabla con un campo que sea una tabla anidada del objeto creado en el punto 2
Ejemplo: queremos almacenar los siguientes datos de nuestros clientes:
Pero nos damos cuenta que un cliente tiene teléfono personal de casa y móvil, teléfono de la empresa y móvil de la empresa, es decir, de 1 a 4 telefónos

1) Creamos un objeto del tipo telefono con 2 campos: tipo (casa, trabajo, fijo, móvil, ...) y el numero
CREATE OR REPLACE TYPE TELEFONO AS OBJECT(tipo varchar2(30),numero number)

2) Creamos un tipo tabla llamado listin que puede contener múltiples objetos del tipo teléfono
CREATE OR REPLACE TYPE LISTIN AS TABLE OF telefono;

3) Creamos una tabla con los datos de un cliente entre los que se encuentra un campo del tipo listin



De esta forma, se crea una tabla en la que el campo TELFS es otra tabla con 2 campos: tipo y numero
IDNOMBREAPELLIDOSTELS
1FranciscoPerez
TIPONUMERO
Casa111
Movil666
Trabajo777
2EsperanzaJimenez
TIPONUMERO
Casa222


Usamos las distintas órdenes SQL con la tabla ortriginal (cliente) y la tabla anidada (tel_tab):

-- Inserto un cliente con 2 telefonos
INSERT INTO cliente (ID,nombre,apellidos,telfs) values (1,'Francisco','Perez',listin(telefono('Casa',111),telefono('Movil',666)));
-- Inserto un cliente con 1 telefono
INSERT INTO cliente (ID,nombre,apellidos,telfs) values (2,'Esperanza','Jimenez',listin(telefono('Casa',222)));
-- Añado un telefono al cliente 1. Ver el uso de la palabra clave TABLE que recibe la tabla anidada
INSERT INTO TABLE (SELECT telfs FROM cliente WHERE id=1) VALUES ('Trabajo',777);
-- Listado de clientes y sus telefonos (desanidamiento)
SELECT t1.nombre,t1.apellidos,t2.tipo,t2.numero FROM cliente t1,TABLE(t1.telfs) t2;
-- Busqueda de un telefono concreto
SELECT t1.nombre,t1.apellidos,t2.tipo,t2.numero FROM cliente t1,TABLE(t1.telfs) t2 WHERE t2.numero=777;
-- Telefonos de Perez
SELECT t2.tipo,t2.numero FROM cliente t1,TABLE(t1.telfs) t2 WHERE t1.apellidos='Perez';
-- Cambio el telefono de casa de Perez
UPDATE TABLE (SELECT telfs FROM cliente WHERE apellidos='Perez') SET numero=112 WHERE tipo='Casa';
-- Borro el telefono Movil de Perez
DELETE FROM TABLE (SELECT telfs FROM cliente WHERE apellidos='Perez') WHERE tipo='Movil';



Acceso a Objetos Oracle desde Java

// Copiar la librería ojdbc6.jar (u ojdbc5.jar) en C:/BlueJ/lib/userlib.
// La librería está en el servidor de Oracle
import java.sql.*;
class Oracle{
    public static void main (String args []) throws Exception {
        DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection conn = DriverManager.getConnection ("jdbc:oracle:thin:@192.168.0.253:1521:asir2", "chema", "chema");
        Statement stmt = conn.createStatement();
        // Accedo a la tabla que tiene el campo nombre(varchar2) y recta(del tipo rectangulo)
        ResultSet rset = stmt.executeQuery("select nombre,recta from rectangulos");
        while (rset.next()){
            // Obtengo el campo nombre
            String nombre=rset.getString(1);
            // Obtengo el objeto rectangulo
            oracle.sql.STRUCT objeto = (oracle.sql.STRUCT) rset.getObject(2);
                // Saco sus atributos   
                Object[] atributos = objeto.getAttributes();
                java.math.BigDecimal alto = (java.math.BigDecimal) atributos[0];
                java.math.BigDecimal ancho = (java.math.BigDecimal) atributos[1];
                // ¿Y para acceder a la function -método- dime_area()?
                // Esto no se hacerlo
            // Imprimo la fila completa
            System.out.println("Nombre: "+nombre+ ". Dimensiones: "+alto+" x "+ancho);
        }
       
        // Para INSERT, DELETE o UPDATE, se usa executeUpdate
        //stmt.executeUpdate("INSERT INTO rectangulos VALUES ('quinto',new rectangulo(5,555))");
       
        // Puedo hacer uso de los métodos en la sentencia SELECT que ataca a Oracle
        rset = stmt.executeQuery("SELECT nombre,r.recta.dime_area() FROM rectangulos r WHERE r.recta.dime_area()>100");
        System.out.println("Rectangulos con area mayor de 100:");
        while (rset.next()){
            System.out.println(rset.getString(1)+" --> "+rset.getInt(2));
        }
        stmt.close();
    }
}