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):
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:
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.
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:
Este método es preferido por los arquitectos de software, ya que permite diseñar el modelo de objetos independientemente del sistema que se utilice para guardar estos objetos.
Aunque tiene la ventaja de que produce un diseño aparentemente más "limpio" desde el punto de vista de los arquitectos, el problema de estos diseños es que a los administradores de bases de datos no les gusta porque los sistemas diseñados así resultan bastante lentos para accesar la base de datos.
Aunque tiene la ventaja de que produce un diseño más eficiente, el problema de este método es que puede provocar que las definiciones semánticas de los objetos parezcan arbitrarias.
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.
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.
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.
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 |
---|---|
persona | id |
detalles | persona_id |
telefonos | persona_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
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
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_id | int |
compania_id | int |
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
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:
nombre | tipo |
---|---|
id | integer |
type | varchar |
nombre | varchar |
largo | integer |
ancho | integer |
ancho | integer |
peso | integer |
Propiedades del Delfín.. | |
mamarias | integer |
Propiedades del Tiburón.. | |
come_hombres | boolean |
dientes | integer |
bocon | boolea |
Con este tipo de tabla, ActiveRecord simplemente utilizará los campos adecuados y regresará el tipo adecuado dependiendo del campo "type".
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:
nombre | tipo |
---|---|
id | integer |
parent_id | integer NULL |
orden | integer |
nombre | varchar |
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