Rexiology::Work

Microsoft, Information Technologies...

News

From Taiwan, living and working at Tokyo, Japan.


follow rextang at http://twitter.com



Rex's Certifications
Rex's Certifications

Site Info



Locations of visitors to this page



Logos & Chicklets


GeoURL


Creative Commons License
This blog is licensed under a Creative Commons License.


Microsoft Sites

Other Sites

Blog pools

Bloggers

My other places

メロメロパーク





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
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: