Download - Persistencia de un modelo de objetos
Persistencia de un Modelo de Objetos
“persistencia: capacidad de almacenar y recuperar el estado de los objetos, de forma que
sobrevivan a los procesos que los manipulan”
UTN – Jorge Ercoli(Docente TSSI)
Formas de persistir un objetoSerialización proceso de convertir el estado de un objeto a un formato que
se pueda almacenar o transportar. Tipos: Binaria – XML – Soap (en .Net)
BD Orientadas a ObjetosPermite persistir las clases tal como fueron diseñadas
BDR BDOO
BD RelacionalesTablas y relaciones – La opción más usada por : std (SQL),X costo y robustez
Arquitectura basada en Capas
Presentación Interacción entre usuario y
aplicación
Lógica del dominio Objetos y reglas de negocio Lógica de los procesos (basado en CU)
Persistencia Servicios para comunicar la capa
lógica con el almacenamiento (BD) Capa de acceso a datos (DAO ó DAC) (Ejs.: ADO.Net – Hibernate)
Modelado de capa lógica Transaction Script
Un procedimiento X acción del usuario (1 controladora del sistema ó 1 X cada CU)
Dificil de aplicar si el dominio es complejo Se ve a la aplicación como una serie de transacciones
Domain Model Diseño OO de los componentes del sistema se incorporan conceptos como herencia y patrones de diseño Problema: GAP entre el modelo de objetos y la BDR Aplicación vista como un set de objetos interrelacionados
Resumiendo : En TS no hacemos un diseño OO, simplemente escribimos métodos por c/solicitud de la IU y los encapsulamos en 1 ó + clases. En DM diseñamos una aplicación OO donde podemos aplicar todo tipo de asociaciones entre objetos (entre ellas la herencia).
Arquitectura de la capa de persistencia
Table Gateway para cada tabla tenemos asociada una implementación de tipo CRUD
(Create-Retrieve-Update-Delete) La estructura de datos que es utilizada gralmente. para comunicarse tanto
con la base de datos como con la capa de lógica, es de tipo Record Set (DataSet en ADO.Net) ó ResultSet (JDBC)
Row Gateway Similar al anterior, aunque se trabaja a nivel de filas de la tabla, por lo que
las operaciones CRUD tienen asociadas implícitamente la clave de la fila Las operaciones de búsqueda pueden ser servicios estáticos de la clase o
pertenecer a una nueva clase
Capa de persistencia, continuación
Active Record Generalmente se comenzo con Row Gateway y se le adicionó lógica del
dominio La búsqueda se hace en otra clase
Data Mapper Abstrae totalmente la lógica de la BD transferir información entre la base de datos y los objetos de la capa de
lógica sin que estos tomen conocimiento
Integración de técnicas de capa lógica con persistencia
T. Script
Domain Model
Row gateway
Table gateway
Active record
Data mapper
Domain Model con Data Mapper = ORM (object relational mapping)
Librería de persistencia de objetos para BD relacionales Arquitectura :
Un ejemplo de ORM: “Nhibernate”, versión del Hibernate para .NETN
Hibernate
Un ORM como Hibernate implica: No trabajar con filas de tablas (DataRows ó RecordSet ó ResultSet) Trabajar con las clases diseñadas en su modelo del dominio Código OO limpio, que permite trabajar con herencia y polimorfismo en nuestras clases de negocio Permite elegir la BD relacional con la que querramos interactuar (SqlServer-PostGre-MySql-DB2, Oracle,...) Genera automáticamente el cód. SQL usando un mapeo objeto-relacional, el cual se especifica en un documento XML Permite crear, modificar, recuperar y borrar objetos persistentes. Al recuperarlos nos permite navegar por las asociaciones entre objs. y luego actualizarlos al finalizar una transacción.
Ejemplo: .......
Venta v=sesion.Load(typeof(Venta), idventa);
v.Lineas.Add(nuevaLinea);
v.Cliente.Saldo=v.Cliente.Saldo + nuevaLinea.Total;
Transaccion.Commit();
Nhibernate – El proceso de desarrollo
1. Crear la clase que necesita ser persistida
2. Crear la tabla para persistir la clase
3. Crear un archivo de mapeo para que NHibernate sepa como persistir las propiedades de la clase
4. Crear un archivo de configuración para que NHibernate sepa como conectarse a su BD.
5. Usar el API del NHibernate
Creamos las clases y las tablas (1 y 2)
Hacemos el mapping (3)<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" default-access="property">
<class name="TPVBO.Venta, TPVBO" table="Venta" ><id name="VentaID" column="idVenta" unsaved-value="0">
<generator class="identity" /></id><property name="Fecha" type = "DateTime" /><property name="Numero" type = "Int32" />
<bag name="LineaVenta" cascade="all"><key column="venta"/><one-to-many class="TPVBO.LineaVenta, TPVBO"/>
</bag></class>
</hibernate-mapping>
Venta.hbm.xml
...................<class name="TPVBO.LineaVenta, TPVBO" table="lineaventa" >
<id name="LineaVentaID" column=“idLineaVta" unsaved-value="0"><generator class="identity" />
</id><property name="Cantidad" type = "Int32" />
<many-to-one name=“Producto" class="TPVBO.Producto, TPVBO"column="producto" />
</class></hibernate-mapping>
LineaVenta.
hbm.xml
Creamos el archivo de configuración (4)<?xml version="1.0" encoding="utf-8" ?><configuration> <configSections> <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </configSections> <nhibernate> <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" /> <add key="hibernate.dialect" value="NHibernate.Dialect.MySQLDialect" /> <add key="hibernate.connection.driver_class" value="NHibernate.Driver.MySqlDataDriver" /> <add key="hibernate.connection.connection_string" value="Server=localhost;Database=TpvHibernate;User ID=jorge;Password=jorge" /> </nhibernate></configuration>
Generalmente en el app.config de la
aplicación
Usamos la API del Nhibernate (5)1. Agregamos una referencia a NHibernate.dll (para usarlo en nuestra aplicación)
2. Creamos un objeto de Configuración
Configuration cfg = new Configuration();
3. Le decimos al Configuration sobre los tipos de objetos que vamos a persistir
cfg.AddAssembly(“TPVBO"); // En TPVBO.dll están mis class con mis class.hbm.xml....
4. Creamos una Fabrica de sesiones (1 X BD) y luego le pedimos una Session y comenzamos una transacción.
ISessionFactory factory = cfg.BuildSessionFactory();
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();
5. Trabajamos con nuestros objetos
Producto p=new Producto();
p.Codigo=“CA123”; p.Descripcion=“Camisa CA”; p.Precio=59.90;
session.Save(p);
6. Grabamos la transacción en la BD y cerramos la sesión.
transaction.Commit();
session.Close();
La Session en NHibernate:
• Es el punto de contacto principal para trabajar con Nhibernate
• Nuestros objetos se asocian a ella para poder realizar operaciones de persistencia (Save, Update, Delete, Load ó Get)
1. Grabar ó actualizar
p.precio = 62.45;
session.SaveOrUpdate(p); // hará un Insert si el ID=0, sino un Update con el ID corresp. al objeto
session.Flush(); // permite bajar todos los cambios a la BD (sin necesidad de tener una transacción)
2. Leer uno o mas objetos
Producto p = session.Load( typeof(Producto),m_ID); // Busca el objeto Producto con ese m_ID
// Usamos el HQL, similar a SQL pero nombrando los atributos de nuestra clase, NO campos de Tabla!
ArrayList misProd = session.Find( “from Producto p where p.Precio > 50”);
3. Borrar
session.Delete(p); // borramos nuestro objeto p de tipo Producto
session.Flush();
Con una session abierta ISession session = factory.OpenSession();
La session
Las asociaciones entre objetos
Definen las relaciones entre las clases Se definen en el archivo de mapeo (clase.hbm.xml) NHibernate usa los mapeos de asociaciones para
grabar y recuperar automaticamente los objetos relacionados :
<one-to-one> – Una Venta tiene un Pedido <one-to-many> – Una Venta tiene muchas lineaVenta <many-to-one> – Una lineaVenta tiene un Producto
(muchas lineaVenta, un Producto) <many-to-many> – Un Empleado tiene muchas Tareas, y
una Tarea tiene muchos Empleados
Mapeando herencia<class name="TPVBO.Producto, TPVBO" table="Producto" >
<id name="ProductoID" column="productoID" unsaved-value="0">
<generator class="identity" />
</id>
<property name="Codigo" type = "String(15)" />
<property name="Descrip" type = "String(40)" />
<property name="Precio" type = "Decimal(10,2)" />
<joined-subclass name="TPVBO.Articulo, TPVBO" table="Articulo">
<key column="ProductoID"/>
<property name="Costo" type = "Decimal(10,2)" />
<property name="Stock" type = "Int32" />
</joined-subclass>
<joined-subclass name="TPVBO.Promocion,TPVBO" table=“Promocion">
<key column="ProductoID"/>
<property name="Validez" type = "DateTime" />
<bag name="Articulos" cascade="all">
<key column="promocion"/>
<one-to-many class="TPVBO.Articulo, TPVBO"/>
</bag>
</joined-subclass>
</class>
Manejo de colecciones// Creo una Venta – En el mapping de venta dice : <bag name="LineaVenta" cascade="all">
Venta v=new Venta();
v.Fecha=hoy; v.Numero=22;
// Creo 2 lineas de venta
LineaVenta l1=new LineaVenta();
l1.Cantidad=10;
l1.Producto=session.Load(typeof(Producto),155);
LineaVenta l2=new LineaVenta();
l2.Cantidad=5;
l2.Producto=session.Load(typeof(Producto),189);
// agrego a la colección de lineas de venta de v, las 2 lineas creadas
v.lineas.Add(l1); v.lineas.Add(l2);
// grabo la venta a la session con sus 2 lineas (por tener el cascade=all): En la BD se inserta una fila en Venta y 2 en LineaVenta
session.Save(v);
session.Flush();
Trabajando con transacciones (la forma usual...)
try {
session = factory.OpenSession();
transaction = session.BeginTransaction();
session.SaveOrUpdate(miObjetoNegocio);
transaction.Commit();
}
catch (Exception ex) {
transaction.Rollback();
}
finally {
session.Close();
}
Y el manejo de la concurrencia?
Se puede agregar un campo “timeStamp” (dateTime) en cada tabla de la BD y un atributo en la clase. Luego en el mapeo (XML):
<version name="Version" column="version" type = "DateTime" />
try { this.sesion.SaveOrUpdate(this.p);
tx.Commit(); }
catch (NHibernate.StaleObjectStateException ex) {
tx.Rollback();
// Otro usr. realizo cambios, pregunto si quiere reLeer
DialogResult resp;
resp=MessageBox.Show(“Este registro ha variado desde la última vez que se recuperó, desea ver su estado actual?", "Aviso", MessageBoxButtons.YesNo);
if(resp == DialogResult.Yes)
this.recuperaDatos(this.p);
}
finally{this.sesion.Close();}