Persistencia del dominio de nuestro ejemplo

Nuestro dominio de objetos

Domain

Nuestro modelo relacional (tomado de Chinook 1.1)

modelo relacional

¿Cómo lo persistimos?

¿Cómo guardamos y recuperamos nuestro dominio de objetos de la una base de datos?... como se imaginarán no estoy pensando en escribir los insert, update, delete o select necesarios sino que, siguiendo en tren de delegar responsabilidades a expertos, usamos un ORM para la persistencia de nuestras entidades, nuestra primera implementación va a ser con nhibernate (nh). Esto quiere decir que vamos a hacer una implementación de DAOs con nh que se va a llamar DAOs.NH.

¿Cómo hacemos para no quedar “atados” a NHibernate?

Sólo vamos a usar (referenciar) nhibernate desde las implementaciones correspondientes (al momento de este post DAOs.NH, DAOs.NH.Castle y BEs.Validations.NHV). Es decir que no vamos a tener referencias a Nhibernate desde otras implementaciones como Models.Impl o Services.Impl. Ni decir que el Core de nuestra aplicación no va a conocerlo (esto es, los contratos, las BEs y los DTOs).

Para ver como usar NHibernate de la forma tradicional (hasta el momento) pueden ver la documentación oficial o los muchos ejemplos que hay en internet.

Configurando NH con ConfOrm

En este post vamos a ver la configuración de NHibernate mediante ConfOrm para nuestro dominio, y para esto vamos a hacer algunos ajustes a la base de datos (db). Es verdad que también podríamos indicar en los mappings (o a ConfOrm) las diferencias entre el modelo de objetos y el modelo relacional, pero como considero que la base de datos también es parte de nuestra aplicación y por lo tanto es factible de refactorización es que decido modificarla. Los scripts para esto están en la carpeta scripts de nuestro código de ejemplo. Dichos cambios consisten en renombrar algunos campos y una tabla, no modificamos en concepto el diseño de la db.

Ahora bien, vamos al código:

ObjectRelationalMapper orm = new ObjectRelationalMapper();

orm.TablePerClass<Album>();
orm.TablePerClass<Artist>();
orm.TablePerConcreteClass<Person>();
orm.TablePerClass<Genre>();
orm.TablePerClass<Invoice>();
orm.TablePerClass<InvoiceLine>();
orm.TablePerClass<MediaType>();
orm.TablePerClass<Playlist>();
orm.TablePerClass<Track>();

orm.Bag<Playlist>(pl => pl.Tracks);
orm.ManyToMany<Playlist, Track>();

Mapper mapper = new Mapper(orm);

Type type = typeof(Track);
HbmMapping mapping = mapper.CompileMappingFor(type.Assembly.GetTypes());

Configuration configuration = new Configuration();
configuration.Configure();

configuration.AddDeserializedMapping(mapping, "DMS_BEs");

return configuration;

Como podemos ver, el objetivo de esto es llegar al objeto Configuration de nh, el cual antes configurábamos a partir de los .hbm.xml embebidos en el assembly o con attributes sobre nuestro dominio. Para ver las distinas opciones de configuración de ConfOrm pueden revisar el blog de Fabio que seguro encontrarán mucho.

¿y como quedaron estos mappings?

No es necesario escribir los mappings en un archivo, pero si queremos verlos podemos:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="DMS.BEs" assembly="DMS.BEs" xmlns="urn:nhibernate-mapping-2.2">
    <class name="Invoice">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <many-to-one name="Customer" />
        <property name="InvoiceDate" />
        <property name="BillingAddress" />
        <property name="BillingCity" />
        <property name="BillingState" />
        <property name="BillingCountry" />
        <property name="BillingPostalCode" />
        <bag name="Lines" inverse="true" cascade="all,delete-orphan">
            <key column="Invoice" on-delete="cascade" />
            <one-to-many class="InvoiceLine" />
        </bag>
    </class>
    <class name="Track">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <property name="Name" />
        <many-to-one name="Album" />
        <many-to-one name="MediaType" />
        <many-to-one name="Genre" />
        <property name="Composer" />
        <property name="Milliseconds" />
        <property name="Bytes" />
        <property name="UnitPrice" />
    </class>
    <class name="Artist">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <property name="Name" />
    </class>
    <class name="Genre">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <property name="Name" />
    </class>
    <class name="MediaType">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <property name="Name" />
    </class>
    <class name="Playlist">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <property name="Name" />
        <bag name="Tracks">
            <key column="playlist_key" />
            <many-to-many class="Track" />
        </bag>
    </class>
    <class name="InvoiceLine">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <many-to-one name="Invoice" />
        <many-to-one name="Track" />
        <property name="UnitPrice" />
        <property name="Quantity" />
    </class>
    <class name="Album">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <many-to-one name="Artist" />
        <property name="Title" />
        <bag name="Tracks" inverse="true" cascade="all,delete-orphan">
            <key column="Album" on-delete="cascade" />
            <one-to-many class="Track" />
        </bag>
    </class>
    <class name="Person" abstract="true">
        <id name="Id" type="Int32">
            <generator class="hilo" />
        </id>
        <property name="FirstName" />
        <property name="LastName" />
        <property name="Address" />
        <property name="City" />
        <property name="State" />
        <property name="Country" />
        <property name="PostalCode" />
        <property name="Phone" />
        <property name="Fax" />
        <property name="Email" />
    </class>
    <union-subclass name="Employee" extends="Person">
        <property name="Title" />
        <property name="BirthDate" />
        <property name="HireDate" />
        <many-to-one name="ReportsTo" />
    </union-subclass>
    <union-subclass name="Customer" extends="Person">
        <property name="Company" />
        <many-to-one name="SupportRepresentant" />
    </union-subclass>
</hibernate-mapping>

1 comentario:

  1. Mi estimado amigo Nelo, nuevamente yo para preguntarte algo que para ti debe ser totalmente basico

    Veo que tus daos tiene un metodo llamado Persist y su implementacion en nh hace uso de session.Persist(), no veo algo como session.SaveOrUpdate para actualizar un custormer por ejemplo, y aqui la pregunta como realizas esta actualizacion si tus dao y su implementacion solo tienen session.Persist()?, o con este metodo alcanza ?

    Saludos y gracias por tus comentarios

    ResponderBorrar