Mapear una enitdad con un campo resultado de una join

19 views
Skip to first unread message

Juan Cuello

unread,
Dec 10, 2009, 12:23:25 PM12/10/09
to NHibernate-Hispano
Hola buenas,

Debido a que los datos por pantalla que debo mostrar, uno de los
campos es una propiedad calculada a partir de datos de otros
registros.

¿Cómo consideráis que es la mejor forma de obtener esa propiedad? ¿Con
una función de base de datos y especificar insert="false" y
update="false"? ¿hacerlo por hql?

¿En el caso de hacerlo por una función de base de datos, alguien puede
pasarme un link de ejemplo de llamada a una función para una
propiedad?

Muchas gracias

Fabio Maulo

unread,
Dec 10, 2009, 4:40:36 PM12/10/09
to nhibernat...@googlegroups.com
Cual es el calculo ?
--
Fabio Maulo

Juan Cuello

unread,
Dec 11, 2009, 3:28:50 AM12/11/09
to NHibernate-Hispano
Hola,

Hay un valor que es un código calculado a partir de datos de tablas
hijas. Es el típico ejemplo de hacer una select de varias tablas para
obtener resultados que no son propiamente un registro de una única
tabla. Además estos datos solo los necesito para consultar, no
necesito hacer CRUD.

Supongo que lo más fácil es crear una clase y especificar la select
mapeando los resultados de la consulta no? Si es así, necesitaría un
ejemplillo de cómo hacerlo.

Gracias

On 10 dic, 22:40, Fabio Maulo <fabioma...@gmail.com> wrote:
> Cual es el calculo ?
>

Juan Cuello

unread,
Dec 11, 2009, 3:30:15 AM12/11/09
to NHibernate-Hispano
Bueno,

También necesito hacer paginación... ya que el resultado puede
devolverme más de 500 registros... y sólo muestro 20 por página.

On 10 dic, 22:40, Fabio Maulo <fabioma...@gmail.com> wrote:
> Cual es el calculo ?
>

José F. Romaniello

unread,
Dec 11, 2009, 1:58:29 PM12/11/09
to nhibernat...@googlegroups.com
Sería algo como poniendolo en un  ejemplo bobo; "Empleado que tiene una collection de Hijos mapeada, necesitas mostrar el nombre del empleado y te interesa una propiedad que es la cantidad de hijos mayores de edad?

Si este es el caso yo haría una propiedad solo lectura en la clase empleado (o en un viewmodel si esto es solo especifico a un caso particular).. Que sea  (pseudo código);

public int CantidadHijosMayoresDeEdad{
get { return Hijos.Count(h => h.FechaNacimiento < DateTime.Today.AddYears(-21));}
}

Asegurate de que haces un fetch join si vas a mostrar esa propiedad de 10 empleados. Tene cuidado con los productos cartesianos.

En este caso que te explico aca; Hijos es una propiedad de Empleado. Qué pasa si es un dato que no es propiedad de la clase que a vos te interesa?... bueno ahí vas a tener que hacer una buena consulta.

Acerca del paginado: 

Si estas usando linq for nhibernate; la base de todo mecanismo de paginación es la siguiente: "Skip(...).Take(..)", Salta(10 registros).Toma(20 registros).

Si estas usando criteria es con SetMaxResult, y SetFirstResult.

Si estas usando hql, no hay. tenes que hacerlo por fuera de la consulta.

Si estas usando SQL directamente, te la debo. Si estas usando SQL Server directamente vas a tener que llamar por teléfono al que invento la característica de paginación para preguntarle que "TECLA" tocar. 



--
Para escribir al Grupo, hágalo a esta dirección: NHibernat...@googlegroups.com
Para más, visite: http://groups.google.com/group/NHibernate-Hispano

Fabio Maulo

unread,
Dec 12, 2009, 7:17:32 AM12/12/09
to nhibernat...@googlegroups.com
El 11 de diciembre de 2009 15:58, José F. Romaniello <jfroma...@gmail.com> escribió:
Errata corrige
 
Si estas usando criteria *o HQL o H-SQL* es con SetMaxResult, y SetFirstResult.


Con H-SQL y MsSQL es posible que en algunos casos tengas problemas 

--
Fabio Maulo

José F. Romaniello

unread,
Dec 12, 2009, 7:38:55 AM12/12/09
to nhibernat...@googlegroups.com
Y otra cosa, en unhaddins hay algo:


Aunque para mi, todo el problema el problema de Paginación/Sorting y Filtering, se puede resolver con dos funciones en un servicio de la siguiente forma:

ICollection<TEntity> GetPage(int size, int pageNumber, Expression<Func<TEntity, bool>> predicate, params Func<TEntity, object>[] order);
int GetLastPageNumber(int size, Expression<Func<TEntity, bool>> predicate);

Y son dos funciones diferentes por que GetLastPageNumber, se llama unicamente cuuando cambia el predicado. Eso da la libertad de en el presenter hacer cualquier cosa.

Juan Cuello

unread,
Dec 14, 2009, 8:46:20 AM12/14/09
to NHibernate-Hispano
Muchas gracias a ambos. Lo de paginación lo tengo solucionado gracias
a vuestras instrucciones. Uso Oracle 10g.

El problema de propiedades calculadas es que los programadores, para
obtener dicho valor, accedían a las subentidades (carga lazy)
provocando que, en una lista de 100 elementos, que se accede a 3
subentidades por elemento, hablamos de 300 consultas a base de
datos...malo malo.

La idea era especificar la select de carga de dicha entidad haciendo
los joins que corresponden y mapeando esos valores, de forma que al
cargar la lista, directamente tendría los valores calculados en esas
propiedades y me ahorraba accesos a bbdd. Pondría estas propiedades en
insert="false" update="false" y listo...

¿Es buena idea hacerlo de esa forma? ¿Alguna idea mejor? ¿Tenéis algún
ejemplo de especificación de select para la carga de una entidad?

Muchas gracias

On 12 dic, 13:38, José F. Romaniello <jfromanie...@gmail.com> wrote:
> Y otra cosa, en unhaddins hay algo:http://code.google.com/p/unhaddins/source/browse/#svn/trunk/uNhAddIns...
>
> Aunque para mi, todo el problema el problema de Paginación/Sorting y
> Filtering, se puede resolver con dos funciones en un servicio de la
> siguiente forma:
>
> ICollection<TEntity> GetPage(int size, int pageNumber,
> Expression<Func<TEntity, bool>> predicate, params Func<TEntity, object>[]
> order);
> int GetLastPageNumber(int size, Expression<Func<TEntity, bool>> predicate);
>
> <http://code.google.com/p/unhaddins/source/browse/#svn/trunk/uNhAddIns...>Y

Carlos Peix

unread,
Dec 14, 2009, 9:15:35 AM12/14/09
to nhibernate-hispano
Hola Juan,

Por que 300 consultas a la base de datos? si tenes tres colecciones hijas deberias tener 3 consultas que devuelven 100 elementos cada una (a menos que me equivoque por mucho)

La primera recomendacion seria no realizar optimizaciones (como la carga "eager" de las colecciones) hasta que no se compruebe que es necesario.

Como siempre, NH no hace magia y si es necesario que tengas un cache de la suma en la entidad padre, pues tendras que hacerlo de la misma manera y persistirlo. Esta opcion deberias usarla en caso extremo ya que implica almacenamiento redundante de informacion, aunque con un buen diseño de los objetos, no deberia ser muy riesgoso.

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

2009/12/14 Juan Cuello <juan.c...@gmail.com>

José F. Romaniello

unread,
Dec 14, 2009, 9:26:32 AM12/14/09
to nhibernat...@googlegroups.com
Un ejemplo sería :

Invoice que tiene las propiedades Customer, Payment, Contact. Las tres son LazyLoad y eso esta bien, pero para esa consulta que tenes que mostrar ese dato calculado que depende de los tres, y ahi tenemos un problema.

Entonces como hacemos?

from Invoice i
inner join fetch i.Customer
inner join fetch i.Payment
inner join fetch i.Contact

3 inner join fetch es un poco mucho.... Pero.

Si en todo tu sistema, por todos lados tenes que mostrar esa propiedad calculada, evaluaría hacerlo de otra forma. Pero vos dijiste que era para mostrar en una grilla paginado solamente.

Saludos

Fabio Maulo

unread,
Dec 14, 2009, 9:39:25 AM12/14/09
to nhibernat...@googlegroups.com
solo para recordarles que esos "join fetch" funciona si las collection son <set>
Fabio Maulo

Juan Cuello

unread,
Dec 14, 2009, 10:02:52 AM12/14/09
to NHibernate-Hispano
Hola,

Aclaro que es una lista de entidades. Cada entidad contiene varios
objetos en relación 1 a 1, por tanto, si recorro la lista y acceso a
ese objeto de cada elemento, estoy haciendo una nueva consulta a base
de datos para cargar el objeto dependiente. Algo así como:

Teniendo esta clase:

public class DataGridRecinte
{
public decimal ID { get; set; }
public string IdentificacioParc { get; set; }
public bool IsValidat { get; set; }
public string SecaRegadiu { get; set; }
public string SupConreada { get; set; }
public string CodiProducte { get; set; }
public List<string> recAjuts;
}

Siendo "data" la lista de entidades de tipo DunRecinte
(List<DunRecinte>) :

IEnumerable<DataGridRecinte> Taularecintes =
from r in data
select new
DataGridRecinte
{
ID = r.ID,

IdentificacioParc = CalcularIdentificacioParcela(r),
recAjuts =
AfegirAjutsALlista(r.RecinteAjuts),
CodiProducte
= GetCodiProducte(r.Varietat),
SecaRegadiu
= r.SecaRegadiuDescripcio,
SupConreada
= FormatSupCultiu(r.SupConreada.GetValueOrDefault(0)),
IsValidat =
GeneralUtils.ParseBool(r.IsValidat)
};

Cada función CalcularIdentificacioParcela, AfegirAjutsALlista, accede
a una subentidad, por tanto, se está haciendo una nueva consulta. Lo
que quisiera yo es obtener directamente la lista
IEnumerable<DataGridRecinte> (o List< DataGridRecinte>). Por eso,
posiblemente lo mejor sería crear la entidad DataGridRecinte,
mapearla, y especificar la select a hacer. De esa forma, con una sola
consulta a base de datos tendría el resultado deseado.

¿Consideráis que es la mejor forma? Además, el resultado es de sólo
lectura... Si así es, me podéis decir un sitio donde haya un ejemplo
de especificación de la select?

¿Alguna otra solución?

Muchas gracias

On 14 dic, 15:39, Fabio Maulo <fabioma...@gmail.com> wrote:
> solo para recordarles que esos "join fetch" funciona si las collection son
> <set>
>
> El 14 de diciembre de 2009 11:26, José F. Romaniello <jfromanie...@gmail.com
>
>
>
>
>
> > escribió:
> > Un ejemplo sería :
>
> > Invoice que tiene las propiedades Customer, Payment, Contact. Las tres son
> > LazyLoad y eso esta bien, pero para esa consulta que tenes que mostrar ese
> > dato calculado que depende de los tres, y ahi tenemos un problema.
>
> > Entonces como hacemos?
>
> > from Invoice i
> > inner join fetch i.Customer
> > inner join fetch i.Payment
> > inner join fetch i.Contact
>
> > 3 inner join fetch es un poco mucho.... Pero.
>
> > Si en todo tu sistema, por todos lados tenes que mostrar esa propiedad
> > calculada, evaluaría hacerlo de otra forma. Pero vos dijiste que era para
> > mostrar en una grilla paginado solamente.
>
> > Saludos
>
> > El 14 de diciembre de 2009 11:15, Carlos Peix <carlos.p...@gmail.com>escribió:
>
> > Hola Juan,
>
> >> Por que 300 consultas a la base de datos? si tenes tres colecciones hijas
> >> deberias tener 3 consultas que devuelven 100 elementos cada una (a menos que
> >> me equivoque por mucho)
>
> >> La primera recomendacion seria no realizar optimizaciones (como la carga
> >> "eager" de las colecciones) hasta que no se compruebe que es necesario.
>
> >> Como siempre, NH no hace magia y si es necesario que tengas un cache de la
> >> suma en la entidad padre, pues tendras que hacerlo de la misma manera y
> >> persistirlo. Esta opcion deberias usarla en caso extremo ya que implica
> >> almacenamiento redundante de informacion, aunque con un buen diseño de los
> >> objetos, no deberia ser muy riesgoso.
>
> >> ----------------------------------
> >> Carlos Peix
>
> >> 2009/12/14 Juan Cuello <juan.cuel...@gmail.com>

José F. Romaniello

unread,
Dec 14, 2009, 11:42:08 AM12/14/09
to nhibernat...@googlegroups.com
Mostra CalcularIdentificacioParcela y DunRecinte (lo mas importante de esta clase).

Gracias.

Juan Cuello

unread,
Dec 14, 2009, 12:44:51 PM12/14/09
to NHibernate-Hispano
Agrego:
Como veis, desde Recinte se accede a Recinte.Municipi.Municipi, a
recinte.Municipi.Municipi.Provincia,... ese es el problema


private string CalcularIdentificacioParcela(DunRecinte
recinte)
{
string _resultat = "{0}{1}-{3}-{4}-{5}-{6}";
string _idProvincia = GeneralUtils.GetFilledNumber
(recinte.Municipi.Municipi.Provincia.CodiProvincia, 2);
string _idMunicipi = GeneralUtils.GetFilledNumber
(recinte.Municipi.Municipi.CodiMunicipi, 3);
string _agregat = GeneralUtils.GetFilledNumber
(recinte.Agregat.GetValueOrDefault(0).ToString(), 3);
string _zona = GeneralUtils.GetFilledNumber
(recinte.Zona.GetValueOrDefault(0).ToString(), 2);
string _poligon = GeneralUtils.GetFilledNumber
(recinte.Poligon, 3);
string _parcela = GeneralUtils.GetFilledNumber
(recinte.Parcela.GetValueOrDefault(0).ToString(), 5);
string _recinte = GeneralUtils.GetFilledNumber
(recinte.Recinte.GetValueOrDefault(0).ToString(), 5);

return String.Format(_resultat, _idProvincia, _idMunicipi,
_agregat, _zona, _poligon, _parcela, _recinte);
}

public partial class DunRecinte
{

private System.Int16? _Agregat;
private System.Int16? _AnyPlanta;
private System.Int16? _AnyRessembra;
private System.Int16? _ArbresAillats;
private System.String _CanviOliveres;
private DunDeclaracio _Declaracio;
private readonly ISet<DunRecAjut> _RecinteAjuts = new
HashedSet<DunRecAjut>();
private readonly ISet<DunRecArbre> _RecinteArbres = new
HashedSet<DunRecArbre>();
private DunCodisEmpresCertif _EmpresCertif;
private System.String _IdFinca;
private System.String _IndArrencada;
private System.String _IsValidat;
private System.Single _MarcPlant1;
private System.Single _MarcPlant2;
private System.Single _MetresCamins;
private System.Single _MetresMur;
private System.String _ModSigpac;
private DunCodisMunicipiCad _Municipi;
private System.Int32? _NumPar;
private System.Int32? _Parcela;
private System.Int32? _ParcelFructicola;
private System.String _PassaportFitosanitari;
private System.String _PeusCitric;
private DunCodisPeus _Peus;
private System.String _Poligon;
private System.Int32? _Recinte;
private System.String _SecaRegadiu;
private DunCodisSistemaFormacio _SistemaFormacio;
private DunCodisSistemaReg _SistemaReg;
private System.Double? _SupConreada;
private DunCodisTinenca _Tinenca;
private DunCodisUs _Us;
private DunCodisVarietat _Varietat;
private System.Int16? _Zona;
private DunCodisSistemaCaptacio _SistemaCaptacio;

public virtual System.Int16? Agregat
{
get
{
return _Agregat;
}
set
{
_Agregat = value;
}
}

public virtual System.Int16? AnyPlanta
{
get
{
return _AnyPlanta;
}
set
{
_AnyPlanta = value;
}
}

public virtual System.Int16? AnyRessembra
{
get
{
return _AnyRessembra;
}
set
{
_AnyRessembra = value;
}
}

public virtual System.Int16? ArbresAillats
{
get
{
return _ArbresAillats;
}
set
{
_ArbresAillats = value;
}
}

public virtual System.String CanviOliveres
{
get
{
return _CanviOliveres;
}
set
{
_CanviOliveres = value;
}
}

public virtual DunDeclaracio Declaracio
{
get
{
return _Declaracio;
}
set
{
_Declaracio = value;
}
}
public virtual ISet<DunRecAjut> RecinteAjuts
{
get
{
return _RecinteAjuts;
}
}

public virtual ISet<DunRecArbre> RecinteArbres
{
get
{
return _RecinteArbres;
}
}

public virtual DunCodisEmpresCertif EmpresCertif
{
get
{
return _EmpresCertif;
}
set
{
_EmpresCertif = value;
}
}


public virtual System.String IdFinca
{
get
{
return _IdFinca;
}
set
{
Check.Require(value != null,"IdFinca must not be null.");
_IdFinca = value;
}
}



public virtual System.String IndArrencada
{
get
{
return _IndArrencada;
}
set
{
_IndArrencada = value;
}
}

public virtual System.String IsValidat
{
get
{
return _IsValidat;
}
set
{
_IsValidat = value;
}
}

public virtual System.Single MarcPlant1
{
get
{
return _MarcPlant1;
}
set
{
_MarcPlant1 = value;
}
}

public virtual System.Single MarcPlant2
{
get
{
return _MarcPlant2;
}
set
{
_MarcPlant2 = value;
}
}

public virtual System.Single MetresCamins
{
get
{
return _MetresCamins;
}
set
{
_MetresCamins = value;
}
}

public virtual System.Single MetresMur
{
get
{
return _MetresMur;
}
set
{
_MetresMur = value;
}
}

public virtual System.String ModSigpac
{
get
{
return _ModSigpac;
}
set
{
_ModSigpac = value;
}
}

public virtual DunCodisMunicipiCad Municipi
{
get
{
return _Municipi;
}
set
{
_Municipi = value;
}
}
public virtual System.Int32? NumPar
{
get
{
return _NumPar;
}
set
{
_NumPar = value;
}
}

public virtual System.Int32? Parcela
{
get
{
return _Parcela;
}
set
{
_Parcela = value;
}
}

public virtual System.Int32? ParcelFructicola
{
get
{
return _ParcelFructicola;
}
set
{
_ParcelFructicola = value;
}
}

public virtual System.String PassaportFitosanitari
{
get
{
return _PassaportFitosanitari;
}
set
{
_PassaportFitosanitari = value;
}
}

public virtual System.String PeusCitric
{
get
{
return _PeusCitric;
}
set
{
_PeusCitric = value;
}
}

public virtual DunCodisPeus Peus
{
get
{
return _Peus;
}
set
{
_Peus = value;
}
}

public virtual System.String Poligon
{
get
{
return _Poligon;
}
set
{
_Poligon = value;
}
}

public virtual System.Int32? Recinte
{
get
{
return _Recinte;
}
set
{
_Recinte = value;
}
}

public virtual System.String SecaRegadiu
{
get
{
return _SecaRegadiu;
}
set
{
_SecaRegadiu = value;
}
}

public virtual DunCodisSistemaFormacio SistemaFormacio
{
get
{
return _SistemaFormacio;
}
set
{
_SistemaFormacio = value;
}
}
public virtual DunCodisSistemaReg SistemaReg
{
get
{
return _SistemaReg;
}
set
{
_SistemaReg = value;
}
}
public virtual System.Double? SupConreada
{
get
{
return _SupConreada;
}
set
{
_SupConreada = value;
}
}

public virtual DunCodisTinenca Tinenca
{
get
{
return _Tinenca;
}
set
{
_Tinenca = value;
}
}
public virtual DunCodisUs Us
{
get
{
return _Us;
}
set
{
_Us = value;
}
}
public virtual DunCodisVarietat Varietat
{
get
{
return _Varietat;
}
set
{
_Varietat = value;
}
}
public virtual System.Int16? Zona
{
get
{
return _Zona;
}
set
{
_Zona = value;
}
}

public virtual DunCodisSistemaCaptacio SistemaCaptacio
{
get
{
return _SistemaCaptacio;
}
set
{
_SistemaCaptacio = value;

Fabio Maulo

unread,
Dec 14, 2009, 7:24:19 PM12/14/09
to nhibernat...@googlegroups.com
le pones unos batch-size y es probable que resuelvas el problema.

2009/12/14 Juan Cuello <juan.c...@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



--
Fabio Maulo

Reply all
Reply to author
Forward
0 new messages