Mapeo evento-rol-actor

11 views
Skip to first unread message

Carlos Peix

unread,
Jun 1, 2011, 8:18:53 PM6/1/11
to nhibernate-hispano
Hola gente,

Quiero consultarles algo ya que estoy tratando de encontrar una forma de mapeo mas o menos estandar para un problema mas o menos estandar en mis modelos. La consulta no solo se relaciona con el mapeo en NH, tambien me interesa determinar la estructura de BD mas adecuada.

Basicamente es la relacion evento-rol-actor, para que quede mas claro, permitanme conpartir algunas clases:

// evento
public class Order {
    Guid _id;
    Customer _customer;
}

// el rol
public class Customer {
    Guid _id;
    Party _actor;    // (1) u Object _actor
}

// actores (ver Fowler para teoria sobre Party/Company/Person y accountability)
public class Company : Party {
    Guid _id;
    string _companyName;
}

public class Person : Party {
    Guid _id;
    string _firstName;
    string _lastName;
}

(1) aqui puedo tener definida alguna clase base para los actores que pueden adoptar el rol de customer (como seria el caso de Party) y otras veces no, para lo cual tendre que usar Object. Entiendo que esto me lleva a dos respuestas distintas a mi consulta.

La pregunta es como me conviene mapear la propiedad _actor de Customer, ya se en el caso en que esta deba ser de tipo Object o Party.

He probado con any y con many-to-one. Este objeto Customer podria o no utilizar el mismo valor para el Id que el actor. Opino que lo mejor seria que no lo hagan. Por ahi con Guids no hay problema pero en otros casos me restringe.

Un punto adicional seria que, probablemente, necesite en la entidad Company o Person una coleccion de todos los roles desempeñados, seria como la relacion opuesta y, en este caso, definitivamente no los roles no van a compartir ninguna clase base salvo, eventualmente, una interfaz "IRol".

----------------------------------
Carlos Peix

Nestor Rodriguez

unread,
Jun 1, 2011, 9:39:48 PM6/1/11
to nhibernat...@googlegroups.com
Yo diria que hagas el mapping de la herencia y haces un <many-to-one class="Party"> en Customer y en Party mapearias la coleccion de Roles.

2011/6/1 Carlos Peix <carlo...@gmail.com>
--
Para escribir al Grupo, hágalo a esta dirección: NHibernat...@googlegroups.com
Para más, visite: http://groups.google.com/group/NHibernate-Hispano

Carlos Peix

unread,
Jun 2, 2011, 5:12:46 AM6/2/11
to nhibernat...@googlegroups.com
Hola Nestor,

Sip, creo que esa es la opcion, en un momento probe una one-to-one desde Customer a Party pero se complica cuando no puedo usar la clase base (el caso en que los actores no hereden de una clase comun).

Muchas gracias

----------------------------------
Carlos Peix

2011/6/1 Nestor Rodriguez <nest...@gmail.com>

Carlos Peix

unread,
Jun 2, 2011, 5:52:44 PM6/2/11
to nhibernate-hispano
Sigo con este tema y tengo una duda con eager-loading. El modelo es el que mostre en el post anterior: Order tiene una referencia a Customer y Customer tiene una referencia a Party.

Mappings:

<class name="Order" table="Orders">
...
<many-to-one name="Customer" class="Customer" column="CustomerId"/>
</class>

<class name="Customer" table="Customers">
...
<many-to-one name="Party" cascade="save-update"
unique="true" column="PartyId"/>
</class>

<import class="Party" />
<class name="Person" table="People">
...
<property name="FirstName"/>
<property name="LastName"/>
</class>

<class name="Company" table="Companies">
...
<property name="CompanyName"/>
</class>

Con esto tengo dos temas, primero que me da un error de compilacion del mapping: "An association from the table Customers refers to an unmapped class: pruebas.nh.modelo.Party", a pesar del import. Que estoy haciendo mal?

Luego me gustaria poder hacer este query (HQL)

"from Order o join fetch o.Customer c join fetch c.Party p"

Puedo hacer eso?

Gracias!

----------------------------------
Carlos Peix

2011/6/1 Carlos Peix <carlo...@gmail.com>

Carlos Cocom

unread,
Jun 2, 2011, 6:16:53 PM6/2/11
to nhibernat...@googlegroups.com
Es necesario mapear la clase base y después los mapping para las sub clases o extensiones esto también se pueden usando <union-subclass> pero esta te crearía una tabla por clase excepto la común

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="pruebas.nh.modelo"
namespace="pruebas.nh.modelo">
<class name="Party">
<id name="Id">
<generator class="guid.comb" />
</id>
<discriminator column="PartyType" />
<property name="Location" />
<property name="Hour" not-null="true" />
</class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="pruebas.nh.modelo"
namespace="pruebas.nh.modelo">
<subclass name="Company" extends="Party">
<property name="CompanyName"/>
</subclass>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="pruebas.nh.modelo"
namespace="pruebas.nh.modelo">
<subclass name="Person" extends="Party">
<property name="FirstName"/>
<property name="LastName"/>
</subclass>
</hibernate-mapping>


Sobre el query no debería haber ningún problema seria cosa que pruebes y comentes



Carlos Peix

unread,
Jun 2, 2011, 7:25:32 PM6/2/11
to nhibernat...@googlegroups.com
Hola Carlos, gracias por la respuesta.

El mapping que sugeris me parece que no es lo que necesito porque requiere una tabla para Party que no es lo que cuiero.

Pude hacerlo de esta manera:

<class name="Party">
<id name="Id">
<generator class="guid.comb"/>
</id>
<union-subclass name="Person" table="People">
<property name="FirstName"/>
<property name="LastName"/>
</union-subclass>
<union-subclass name="Company" table="Companies">
<property name="CompanyName"/>
</union-subclass>
</class>

Pero el query me da este error:

Invalid join: c.Party [from Order o join fetch o.Customer c join fetch c.Party p]

Insisto, lo que yo quiero es cargar Order, su Customer y su Party eager. Se puede?

----------------------------------
Carlos Peix

2011/6/2 Carlos Cocom <drevil...@gmail.com>

Nestor Rodriguez

unread,
Jun 2, 2011, 9:22:43 PM6/2/11
to nhibernat...@googlegroups.com
  1. Bueno si mapeas a Party como Abstract = "True" no te genenera una tabla.  Tambien podrias elegir mapear la herencia con <subclass...>.
  2. El <import> se usa cuando quieres hacer una proyeccion de una consulta en un objeto ej "select new Party(c.Id, c.Name) from ClassC c"
  3. En el mapping del many to one te hace falta colocar el "class = "Party".
  4. Y el query que quieres hacer si es posible, quizas prueba "select o from Order o join fetch o.Customer c join fetch c.Party".
  5. Estas seguro que UNICAMENTE un cliente puede tener un party ? porque si no es asi debes quitar el "unique = true".  El unique genera un constraint en la llave foranea de cliente -> party
Saludos,
Nestor Andres Rodriguez

2011/6/2 Carlos Peix <carlo...@gmail.com>

Carlos Peix

unread,
Jun 2, 2011, 9:35:17 PM6/2/11
to nhibernat...@googlegroups.com
Hola Nestor,

2011/6/2 Nestor Rodriguez <nest...@gmail.com>

  1. Bueno si mapeas a Party como Abstract = "True" no te genenera una tabla.  Tambien podrias elegir mapear la herencia con <subclass...>.
Si no me equivoco sugeris este mapping:

<class name="Party" abstract="true">
<id name="Id">
<generator class="guid.comb"/>
</id>
</class>

<subclass name="Person" extends="Party" table="People">
<property name="FirstName"/>
<property name="LastName"/>
</subclass>

<subclass name="Company" extends="Party" table="Companies">
<property name="CompanyName"/>
</subclass>

Pero falla con este error:  "XML validation error: The 'table' attribute is not declared.", asumo que se debe al primer mapping

  1. En el mapping del many to one te hace falta colocar el "class = "Party".
Pense que no hacia falta ya que la leer del tipo de la propiedad. Igual no cambia anda agregando eso. 

  1. Y el query que quieres hacer si es posible, quizas prueba "select o from Order o join fetch o.Customer c join fetch c.Party".
Ese query falla con el mismo error que mencione antes. 

  1. Estas seguro que UNICAMENTE un cliente puede tener un party ? porque si no es asi debes quitar el "unique = true".  El unique genera un constraint en la llave foranea de cliente -> party
Si, estoy seguro y es por diseno. Cuando creo el rol Customer es para uno y solo un Party (Person o Company). es una relacion one-to-one pero he visto que algunos recomiendan una many-to-one con el unique="true"

Saludos,
Nestor Andres Rodriguez


Muchas gracias

Nestor Rodriguez

unread,
Jun 2, 2011, 11:51:57 PM6/2/11
to nhibernat...@googlegroups.com
1.  Si incluyes abstract=true en un mapeo <union-sub-class> no te va a generar tabla para la clase abstracta.
2.  El incluir el abstract=true en un mapeo de <subclass> DEBES colocar la tabla en la definicion de la clase <class name=Party table=Parties> ya que esta estrategia persiste todo en una sola tabla.
3. Me surge la duda de si NHibernate te coloca automaticamente un <discriminator-column> y los <discriminator-values> cuando mapeas el <class> y las <subclass> respectivamente.  Pensaria que deberias incluirlos.
4. Despues de esto verifica de nuevo el query.

Saludos

Carlos Peix

unread,
Jun 3, 2011, 5:06:59 AM6/3/11
to nhibernat...@googlegroups.com
Hola Nestor,

2011/6/3 Nestor Rodriguez <nest...@gmail.com>

1.  Si incluyes abstract=true en un mapeo <union-sub-class> no te va a generar tabla para la clase abstracta.

Supongo que quisiste escribir <union-subclass>, en ese caso, me queda el mapeo que copio a continuacion. No se requiere colocar abstract="true" y, efectivamente, no requiere una tabla para Party. Esto ya lo habia comentado en un post anterior.

<class name="Party">
<id name="Id">
<generator class="guid.comb"/>
</id>
<union-subclass name="Person" table="People">
<property name="FirstName"/>
<property name="LastName"/>
</union-subclass>
<union-subclass name="Company" table="Companies">
<property name="CompanyName"/>
</union-subclass>
</class>

2.  El incluir el abstract=true en un mapeo de <subclass> DEBES colocar la tabla en la definicion de la clase <class name=Party table=Parties> ya que esta estrategia persiste todo en una sola tabla.

Entiendo que aca estaas hablando de este otro enfoque para la persistencia:

<class name="Party" abstract="true">
<id name="Id">
<generator class="guid.comb"/>
</id>
</class>

<subclass name="Person" extends="Party" table="People">
<property name="FirstName"/>
<property name="LastName"/>
</subclass>

<subclass name="Company" extends="Party" table="Companies">
<property name="CompanyName"/>
</subclass>
 
En este caso no persiste todo en una sola tabla sino que es una tabla por cada clase (incluso la abstracta).

3. Me surge la duda de si NHibernate te coloca automaticamente un <discriminator-column> y los <discriminator-values> cuando mapeas el <class> y las <subclass> respectivamente.  Pensaria que deberias incluirlos.

Entiendo que el discriminator solo es necesario en caso de que tengas una tabla para la clase abstracta o una tabla para toda la jerarquia.
 
4. Despues de esto verifica de nuevo el query.

Ya he hecho la verificacion con estos dos mappings y no funciona en ninguno de los dos casos, siempre con el mismo error.
 

Saludos

nelopa...@gmail.com

unread,
Jun 3, 2011, 8:56:46 AM6/3/11
to nhibernat...@googlegroups.com
Para el mapping y las tablas:
 http://nhforge.org/doc/nh/en/index.html#inheritance-strategies

y dale una mirada a la tabla de 8.1 (http://nhforge.org/doc/nh/en/index.html#inheritance-limitations) que te puede ayudar a decidir que estrategia de mapeo usar.

saludos.
nelo

2011/6/3 Carlos Peix <carlo...@gmail.com>

--

Nestor Rodriguez

unread,
Jun 3, 2011, 11:14:37 AM6/3/11
to nhibernat...@googlegroups.com

Bueno te comento,

Utilizando NH 3.0 hice el mapeo que tu sugeriste y luego de algunos ajustes

ya funciona igual que el query.  Los ajustes que hice fueron:

1- Cambie los campos a propiedades para hacerlo mas rapido, no significa que

toque hacerlo con propieades solo que tendrias que colocar access="field".

2- En el mapeo de <union-subclass> si fue necesario colocar Abstract =

"True" de lo contrario verificando con el SchemaExport SI generaria una

tabla para Party.

3- En el mapeo de Customer->Party el <many-to-one> tiene el property ="Party" pero deberia ser property="Actor".

4- En tu clase de Customer existe una propiedad de tipo Party llamada Actor.

 Asi que tienes que cambiar el query para que el join sea c.Actor en lugar de c.Party.
 
Cuentame si te funciona.
 
Nestor Andres Rodriguez

<class name="Party" abstract="true">
    <id name="Id">
      <generator class="guid.comb"/>
    </id>

    <union-subclass name="Person" table="People">
      <property name="FirstName"/>
      <property name="LastName"/>
    </union-subclass>
    <union-subclass name="Company" table="Companies">
      <property name="CompanyName"/>
    </union-subclass>
  </class>

  <class name="Customer" table="Customers">


    <id name="Id">
      <generator class="guid.comb"/>
    </id>

    <many-to-one name="Actor" cascade="save-update"


          unique="true" column="PartyId"/>
  </class>

  <class name="Order" table="Orders">


    <id name="Id">
      <generator class="guid.comb"/>
    </id>

    <many-to-one name="Customer" class="Customer" column="CustomerId"/>
  </class>


public class Order
    {
        public virtual Guid Id
        {
            get;
            set;
        }

        public virtual Customer Customer
        {
            get;
            set;
        }
    }

    public class Party
    {
        public virtual Guid Id
        {
            get;
            set;
        }
    }

    public class Customer
    {
        public virtual Guid Id
        {
            get;
            set;
        }

        public virtual Party Actor
        {
            get;
            set;
        }
    }

    public class Company : Party
    {
        public virtual string CompanyName
        {
            get;
            set;
        }
    }


    public class Person : Party
    {
        public virtual string FirstName
        {
            get;
            set;
        }
        public virtual string LastName
        {
            get;
            set;
        }
    }


////// Program.cs

static void Main(string[] args)
        {
            var configuration = new Configuration().Configure();
            new SchemaExport(configuration).Create(true, true);
            var sessionFactory = configuration.BuildSessionFactory();

            using (var session = sessionFactory.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    var result = session.CreateQuery("select o from Order o join fetch o.Customer c join fetch c.Actor p").List<Domain.Order>();
                    Console.WriteLine(result.Count);
                }
            }

            Console.ReadLine();
        }

Carlos Peix

unread,
Jun 3, 2011, 5:46:09 PM6/3/11
to nhibernat...@googlegroups.com
Hola Nelo,

Quizas me estas sugiriendo un RTFM? :-)

Creo que los mapeos que tengo a disposicion los he podido configurar. Lo que me gustaria saber es cual de todas las opciones es mas recomendable para el escenario.

Gracias

----------------------------------
Carlos Peix

Carlos Peix

unread,
Jun 4, 2011, 9:52:36 AM6/4/11
to nhibernat...@googlegroups.com
Hola Nestor, te agradezco inmensamente el tiempo que has dedicado.


2011/6/3 Nestor Rodriguez <nest...@gmail.com>

2- En el mapeo de <union-subclass> si fue necesario colocar Abstract =

"True" de lo contrario verificando con el SchemaExport SI generaria una

tabla para Party.

Estas en lo cierto, como en este proyecto tengo activada la generacion automatica del esquema, no me habia dado cuenta. Agregue el abstract="true" y ya no genera la tabla Party.
 

3- En el mapeo de Customer->Party el <many-to-one> tiene el property ="Party" pero deberia ser property="Actor".

4- En tu clase de Customer existe una propiedad de tipo Party llamada Actor.

 Asi que tienes que cambiar el query para que el join sea c.Actor en lugar de c.Party.

Es cierto, esto fue un error mio ya que luego de un par de mails pegue el nuevo mapping sobre un modelo ya cambiada. De otra manera no hubiese compilado el mapping. Disculpas por la confusion.

Repase tus recomendaciones en mi codigo y todo estaba igual, la cosa es que ahora el query funciona. Estuve revisando varias versiones en el repo local de Hg y no encuentro ninguna cambio determinante. Creo que podemos llamarlo magia.

Pego el codigo definitivo para referencia de los demas:

Modelo:

public abstract class Party : Entity {
...
}

public class Company : Party {
...
}

public class Person : Party {
...
}

public class Customer : Entity {
private Party party;
...
}

public class Order : Entity
{
private Customer customer;
...
}

Mappings:

<class name="Party" abstract="true">
<id name="Id">
<generator class="guid.comb"/>
</id>
<union-subclass name="Person" table="People">
<property name="FirstName"/>
<property name="LastName"/>
</union-subclass>

<union-subclass name="Company" table="Companies">
<property name="CompanyName"/>
</union-subclass>
</class>

<class name="Customer" table="Customers">
<id name="Id">
<generator class="guid.comb"/>
</id>

<many-to-one name="Party" cascade="save-update"
unique="true" column="PartyId"/>
</class>

<class name="Order" table="Orders">
<id name="Id">
<generator class="guid.comb"/>
</id>

<property name="Ammount"/>
<many-to-one name="Customer" column="CustomerId"/>
</class>

Muchas gracias Nestor, de debo una cerveza.

----------------------------------
Carlos Peix

Nestor Rodriguez

unread,
Jun 4, 2011, 12:49:05 PM6/4/11
to nhibernat...@googlegroups.com
De nada.

PS:  Todos son testigos de la promesa de la cerveza :P

2011/6/4 Carlos Peix <carlo...@gmail.com>

Carlos Cocom

unread,
Jun 4, 2011, 1:19:00 PM6/4/11
to nhibernat...@googlegroups.com
Le salio barato

La próxima que sea un barril :P

Carlos Peix

unread,
Jun 4, 2011, 2:06:07 PM6/4/11
to nhibernat...@googlegroups.com
La unica condicion es que me lo recuerdes cuando nos veamos.

----------------------------------
Carlos Peix

2011/6/4 Nestor Rodriguez <nest...@gmail.com>

Nestor Rodriguez

unread,
Jun 4, 2011, 2:14:32 PM6/4/11
to nhibernat...@googlegroups.com
Ah ! ya la promesa se condiciono ! 

2011/6/4 Carlos Peix <carlo...@gmail.com>
Reply all
Reply to author
Forward
0 new messages