Rexiology::Work

Microsoft, Information Technologies...

Community

Recent Posts

Tags

News

Email Notifications

    Microsoft Sites

    Other Sites

    Blog pools

    Bloggers

    My other places

    Archives

    NHibernate Tips - composite-id behaviors different after 1.0

     

    Just encountered this problem. in previous projects when using a composite-id tag to map a table with composite keys, it's just as simple as to write the mapping with key-property tag for the keys in table. after starting using 1.0 release, it's now different. there will be error message said:

    composite-id class must override Equals()

    A search to Google got some information about this.

    if one just ignores composite primary keys in the table and mapping one of the composite keys in the mapping file as primary key, the returning objects after query will be missing data with the same primary key specified in mapping file and only return one of them, means it will group the data with the primary key specified in mapping file and return only one row.

    Seems now if one is going to map a composite-id , the composite-id keys must self-form to a component that implements Serializable, Equals(), and HashCode() to self-identify itself as a key. as showing in articles here , here , and here .

    Sounds like not very convenient to map a composite-primary-keys-table to object now, even just for retriving data out of database. to fast workaround this. just came out a quick way to avoid using a composite-id mapping.

    Using a view to form the primary key field which containing the string of those composite primary keys with seperator. for example, if the composite primary keys are field1 and field2 , just create a view with all the fields of original table plus a field, namely like "PKey", which contains concat of field1 + "_" + field2 , that can uniquely identify a row in the view, just like what a composite primary keys did. for a quick mapping of how to write sql in different database server to concat string of different fields, check out this article.

    by using this quick workaround, the mapping file back to the simple form, just have a id tag with the PKey field as primary key, and the objects back from HQL query go normal just as expected.

    This quick workaround should only be ok for doing functions that just retrive data out from a data store to map to the domain objects. this should not be a good way for mapping data that need writing data back to the data store. be cautious about this!

    Technorati Tags: hibernate , nhibernate , orm , database , programming

     

    Comments

    Rexiology::Work said:

     
    just a reminder of forming a sql query.
    for concating strings in different databases, refer...
    # January 21, 2006 6:57 PM

    Rexiology... said:

     
    crosspost from http://rextang.net/blogs/work/
    just a reminder of forming a sql query.
    for concating...
    # January 21, 2006 6:59 PM

    P. Bos said:

    The manual of Nhibernate 1.2 also discourages the usuage of composite keys. However with two tables with a many to many relationship it is normal in database design to use a third table with a primary key composed of two keys, referencing the two tables. E.g. A table with UserID and RoleID as primary fields to function as a joining table between a Users and Rolls table. (one user can have many roles, one role can have many users).

    Using a third unique field that concatenates the two fields in the table is in my opion not good database design. So how would you address this problem in Nhibernate to make the CRUD operations and cascading operatings possible ?

    # November 9, 2007 5:44 AM

    Milton Aguilar A. said:

    <Serializable()> _

    Public Class EmpleadoEntity

       Implements IEquatable(Of EmpleadoEntity)

       'Implementacion

       Public Function Equals1(ByVal other As EmpleadoEntity) As Boolean Implements System.IEquatable(Of EmpleadoEntity).Equals

           If (Me.Equals(other)) Then

               Return True

           Else

               Return False

           End If

       End Function

       ' Clase

       Private f_clave_empleado As PKEmpleado

       Private f_nombre_usuario As String

       Private f_apellidos_usuario As String

       Private f_edad As Integer

       Public Overridable Property CLAVE_EMPLEADO() As PKEmpleado

           Get

               Return Me.f_clave_empleado

           End Get

           Set(ByVal value As PKEmpleado)

               Me.f_clave_empleado = value

           End Set

       End Property

       Public Overridable Property NOMBRE_USUARIO() As String

           Get

               Return Me.f_nombre_usuario

           End Get

           Set(ByVal value As String)

               Me.f_nombre_usuario = value

           End Set

       End Property

       Public Overridable Property APELLIDOS_USUARIO() As String

           Get

               Return Me.f_apellidos_usuario

           End Get

           Set(ByVal value As String)

               Me.f_apellidos_usuario = value

           End Set

       End Property

       Public Overridable Property EDAD() As Integer

           Get

               Return Me.f_edad

           End Get

           Set(ByVal value As Integer)

               Me.f_edad = value

           End Set

       End Property

    End Class

    <Serializable()> _

    Public Class PKEmpleado

       Implements IEquatable(Of PKEmpleado), IEqualityComparer

       ' Implements Interfases

       Sub New()

       End Sub

       Sub New(ByVal codigoUsuario As String, ByVal codigoEmpleado As String)

           Me.f_codigo_empleado = codigoUsuario

           Me.f_codigo_usuario = codigoEmpleado

           Return

       End Sub

       Public Function Equals1(ByVal x As Object, ByVal y As Object) As Boolean Implements System.Collections.IEqualityComparer.Equals

           If (x = y) Then

               Return True

           End If

           If ((x = Nothing) Or (y = Nothing)) Then

               Return False

           End If

           Return x.Equals(y)

       End Function

       Public Function GetHashCode1(ByVal obj As Object) As Integer Implements System.Collections.IEqualityComparer.GetHashCode

           Return obj.GetHashCode()

       End Function

       Public Function Equals2(ByVal other As PKEmpleado) As Boolean Implements System.IEquatable(Of PKEmpleado).Equals

           If (Me.Equals(other)) Then

               Return True

           Else

               Return False

           End If

       End Function

       Public Overrides Function GetHashCode() As Integer

           Dim hash As Integer = 57

           hash = 27 * hash * f_codigo_empleado.GetHashCode()

           hash = 27 * hash * f_codigo_usuario.GetHashCode()

           Return hash

           'Return MyBase.GetHashCode()

       End Function

       Public Overloads Function Equals(ByVal obj As Object) As Boolean

           If (Me Is obj) Then

               Return True

           End If

           If ((obj Is Nothing) OrElse (Not obj.GetType Is MyBase.GetType)) Then

               Return False

           End If

           Dim class2 As PKEmpleado = DirectCast(obj, PKEmpleado)

           Return ((((Not class2 Is Nothing) AndAlso Me.f_codigo_empleado.Equals(class2)) AndAlso (Me.f_codigo_usuario = class2.f_codigo_usuario)))

       End Function

       ' Funciones Locales

       Private f_codigo_usuario As String

       Private f_codigo_empleado As String

       Public Overridable Property CODIGO_USUARIO() As String

           Get

               Return Me.f_codigo_usuario

           End Get

           Set(ByVal value As String)

               Me.f_codigo_usuario = value

           End Set

       End Property

       Public Overridable Property CODIGO_EMPLEADO() As String

           Get

               Return Me.f_codigo_empleado

           End Get

           Set(ByVal value As String)

               Me.f_codigo_empleado = value

           End Set

       End Property

    End Class

    /*****************************/

    <?xml version="1.0" encoding="utf-8" ?>

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Remotemp.Entity" namespace="Remotemp.Data">

    <class name="Remotemp.Entity.EmpleadoEntity" table="dbo.Empleado" >

       <composite-id name="CLAVE_EMPLEADO" class ="Remotemp.Entity.PKEmpleado" unsaved-value="none">

         <key-property name="CODIGO_USUARIO" type="string" column="CODIGO_USUARIO"/>

         <key-property name="CODIGO_EMPLEADO" type="string" column="CODIGO_EMPLEADO"/>

    </composite-id>

    <property name="NOMBRE_USUARIO" type="string" not-null="true"/>

    <property name="APELLIDOS_USUARIO" type="string" not-null="true"/>

    <property name="EDAD" type="int" not-null="true"/>

    </class>

    </hibernate-mapping>

    /********************************/

    USE [NHHIBERNATE]

    GO

    /****** Objeto:  Table [dbo].[USUARIO]    Fecha de la secuencia de comandos: 08/07/2008 08:13:01 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    SET ANSI_PADDING ON

    GO

    CREATE TABLE [dbo].[USUARIO](

    [Codigo_Usuario] [varchar](10) COLLATE Modern_Spanish_CI_AS NOT NULL,

    [Nombre_Usuario] [varchar](50) COLLATE Modern_Spanish_CI_AS NULL,

    [Apellidos_Usuario] [varchar](50) COLLATE Modern_Spanish_CI_AS NULL,

    [Edad] [int] NULL,

    CONSTRAINT [PK_Usuario] PRIMARY KEY CLUSTERED

    (

    [Codigo_Usuario] ASC

    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

    ) ON [PRIMARY]

    GO

    SET ANSI_PADDING OFF

    USE [NHHIBERNATE]

    GO

    /****** Objeto:  Table [dbo].[EMPLEADO]    Fecha de la secuencia de comandos: 08/07/2008 08:12:50 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    SET ANSI_PADDING ON

    GO

    CREATE TABLE [dbo].[EMPLEADO](

    [Codigo_Usuario] [varchar](10) COLLATE Modern_Spanish_CI_AS NOT NULL,

    [Codigo_Empleado] [varchar](10) COLLATE Modern_Spanish_CI_AS NOT NULL,

    [Nombre_Usuario] [varchar](50) COLLATE Modern_Spanish_CI_AS NULL,

    [Apellidos_Usuario] [varchar](50) COLLATE Modern_Spanish_CI_AS NULL,

    [Edad] [int] NULL,

    CONSTRAINT [PK_Empleado] PRIMARY KEY CLUSTERED

    (

    [Codigo_Usuario] ASC,

    [Codigo_Empleado] ASC

    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

    ) ON [PRIMARY]

    GO

    SET ANSI_PADDING OFF

    # August 7, 2008 1:35 PM