Mapeo de Objetos a Relaciones

El Modelo Relacional vs  El Modelo de Objetos

Las bases de datos SQL funcionan en modo relacional. Esto quiere decir que utilizan una Matriz de celdas para representar los datos. En cada renglón de una matriz, existe un registro individual. Cada columna de la matriz representa uno de los valores de ese registro, y tiene un tipo específico (numerico, caracteres, etcetera):

Tabla: Personas

id Nombre Apellido Sexo Nacido En
1 Elvis Presley M Nashville, Tenesee
2 Albert Einstein M Württemberg, Alemania

En el modelo relacional, dos tablas pueden estar relacionadas a traves de una clave de identidad, entre otras cosas para poder representar valores repetidos, o valores que no pertenecen a la naturaleza de lo que representa la tabla:

Tabla: Telefonos

id persona_id Pais Area Telefono
1 2 52 777 334-2340
2 2 49 30 495-2030
3 1 1 615 394-2304

A traves de una de las columnas, la tabla se relaciona.

Sin embargo, en el modelo de objetos, los registros son instancias de un tipo de objeto. En el mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que es parte del lenguaje (por ejemplo String) y un tipo que representa registros para la base de datos (como Persona o Telefono). Además, en el modelo de objetos los tipos tienen referencias directas:

Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos para que pueda ser automáticamente mantenido por la base de datos. Los sistemas de mapeo de objetos típicamente saben como mapear los tipos innatos del lenguaje, como string e int. Estos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datos relacionales.

Aproximaciones al problema

Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos de objetos a las tablas, columnas y tipos de datos, podemos imaginar dos tipos de aproximaciones:

ActiveRecord

Ruby utiliza la librería ActiveRecord para mapear objetos a relaciones. Sus clases heredan de ActiveRecord::Base. Si usted ya ha seguido el tutorial acerca de cómo crear un módulo MVC, usted sabrá que el método que utiliza es el método de tabla a objeto. Sin embargo, usted puede cambiarle de nombre a las columnas y las tablas para crear un modelo semántico más claro.

Definiendo nombres de columnas y tablas

En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el método table_name() del objeto, de la manera siguiente:

  class Personas < ActiveRecord::Base
    def self.table_name() "GENTES" end
  end

Para cambiar nombres de columnas (y para hacer campos calculados), usted puede simplemente definir métodos para obtener y cambiar los valores en un patrón de fachada:

  class Personas < ActiveRecord::Base

    def nombre() 
      read_attribute("G_NOMBRE") 
    end
		
    def nombre=(elnombre) 
      write_attribute("G_NOMBRE", elnombre) 
    end
		
    def nombre_completo()
      nombre() +" "+ apellido()
    end
		
  end

En este ejemplo, el programador ha añadido una fachada para que la columna "G_NOMBRE" responda a la propiedad "nombre" en el objeto. Además, se ha añadido una propiedad "nombre_completo" que formatea el nombre y apellido.

Nota: Si usted tiene control del diseño de base de datos así como de los objetos, es más recomendable utilizar los nombres de las tablas y columnas de tal manera que código de mapeo no sea necesario, como lo vió en el tutorial de módulo MVC.

De esta manera su código es más sencillo de mantener.

Definiendo Relaciones Entre Objetos

Vimos hace unos momentos que en el modelo relacional, las relaciones entre dos tipos son representadas utilizando columnas relacionadas. Ahora veremos este y varios otros tipos de relaciones.

Relaciones Circulares

Las relaciones de pertenencia se representan con has_one o con has_many, lo cual le permite especificar que la relación es de uno a uno o de uno a muchos, respectivamente. La relación opuesta se puede representar con belongs_to, en la clase de destino. El resultado se ve muy natural:

class Persona < ActiveRecord::Base
  has_one :detalle
  has_many :telefono
end

class Detalle < ActiveRecord::Base
  belongs_to :persona
end

class Telefono < ActiveRecord::Base
  belongs_to :persona
end

Esto requiere que los siguientes nombres de columnas por defecto:

Tabla Columna
personaid
detallespersona_id
telefonospersona_id

Por supuesto, si es necesario usted puede cambiar los nombres de columnas por defecto por medio de modificar las opciones de belongs_to. En el siguiente ejemplo cambiaremos el nombre de la columna que conecta a la persona en la clase de detalles:

class Detalle < ActiveRecord::Base
  belongs_to :persona, :foreign_key=>"humano_id"
end

Relaciones Circulares

La posibilidad de cambiar opciones en belongs_to permite, entre otras cosas crear relaciones circulares, por ejemplo:

class Persona < ActiveRecord::Base

  belongs_to :padre, :class_name=>"Persona", :foreign_key=>"padre_id"
  belongs_to :madre, :class_name=>"Persona", :foreign_key=>"madre_id"

end

Asociaciones con tabla de Pivote

Un tipo de relaciones muy útil en el mundo de SQL es la relación de "muchos a muchos", lo cual se logra con lo que se llama una tabla de pivote. Supongamos que tenemos una relación de Personas y Compañias. Una persona puede ser cliente de varias compañías. Y las compañías pueden tener varios clientes. La tabla de pivote es como sigue:

companias_personas tipo
persona_idint
compania_idint

Esta relación se puede representar con has_and_belongs_to_many de la manera siguiente:

class Compania < ActiveRecord::Base
  has_and_belongs_to_many :personas
end

class Persona < ActiveRecord::Base
  has_and_belongs_to_many :companias
end

Definiendo Jerarquías de Objetos y Herencia

Las jerarquías de objetos en sistemas de mapeo relacional se pueden representar automáticamente con una sola tabla. Para lograrlo, su tabla necesita tener un campo llamado type, que representará el tipo del objeto, y los campos de la jerarquía completa deben estar definidos.

Por ejemplo, para representar los campos de la jerarquía de objetos de nuestro ejemplo en la sección teoría de objetos (una jerarquía de pescado->Tiburón->Delfín), podríamos definir una tabla como sigue:

Tabla: Pescados

nombre tipo
idinteger
typevarchar
nombrevarchar
largointeger
anchointeger
anchointeger
pesointeger
Propiedades del Delfín..
mamariasinteger
Propiedades del Tiburón..
come_hombresboolean
dientesinteger
boconboolea

Con este tipo de tabla, ActiveRecord simplemente utilizará los campos adecuados y regresará el tipo adecuado dependiendo del campo "type".

Árboles

Un árbol es una representación donde los objetos tienen un sólo padre. Para definir un árbol en ActiveRecord, utilizamos el código acts_as_tree y definimos un campo llamado parent_id que permita valores nulos:

Tabla: Categorias

nombre tipo
idinteger
parent_idinteger NULL
ordeninteger
nombrevarchar

Con esto, podemos definir la tabla, e incluso podemos utilizar opciones para mantener el orden de los nodos hijos, así como para mantener una cuenta de hijos.

class Categoria < ActiveRecord::Base
  acts_as_tree :order => "orden", :counter_cache => true
  
end