These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: How do I use two columns as generator ID
PostPosted: Tue Apr 15, 2008 2:39 am 
Newbie

Joined: Thu Apr 03, 2008 4:16 am
Posts: 18
Need help with Hibernate? Read this first:
http://www.hibernate.org/ForumMailingli ... AskForHelp

Hibernate version:

Mapping documents:

Code between sessionFactory.openSession() and session.close():

Full stack trace of any exception that occurs:

Name and version of the database you are using:

The generated SQL (show_sql=true):

Debug level Hibernate log excerpt:


Problems with Session and transaction handling?

Read this: http://hibernate.org/42.html

Currently I am supporting a legacy app where I am trying to use nHibernate. We have two columns one of which is ID and other as number which need to be generated by a custom class by implementing IIdentifierGenerator. After reading the docs and going throught most of the forumsn I find I can apply generated to only one column per entity class or DTO. So is there any possible way I can achive the same. I have read about generated properties but the the generation is taken care of iin DB in my case generation has to happen in a custom counter class. Any help would be appreciated

Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 5:09 am 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
I think you may have the same issue as I did. We have a legacy system with primary keys consisting of an identity column (new values returned from the database) and a char(2) column (set by the user before saving). NHibernate can't support this out of the box, but I've got it working using a customer Persister (you can specify this in the mapping file) and two minor changes to the NHibernate sources (making things overridable that were previously private). You're welcome to take a look at this, but for the moment it's not compatible with the new NH2, as the relevant area of NH has completely changed. I'll be looking into porting it when I get a chance.

Kev


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 5:12 am 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
Actually, after reading your problem again, I'm not sure they're that similar, but if nobody suggests anything better I'm pretty sure you could adapt what I've done to support your requirement


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 7:18 am 
Newbie

Joined: Thu Apr 03, 2008 4:16 am
Posts: 18
Kev
Thanks for letting me know about this alternate method. I would also want to check the way you have implemented but then my next question would be if i change nhibernate source code wouldnt I have to expose my source code which is a proprietary software due GPL licensing. All said and done could you give me details for your implementation


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 7:20 am 
Newbie

Joined: Thu Apr 03, 2008 4:16 am
Posts: 18
Also one more thing i would like to add was I have no problem appending stuff to what is coming from database implementing IIdentifiergenerator but problem is when a entity class has two of such columns

example PersonID and PersonNumber both generated from a custom class


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 8:01 am 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
Hi,
I needed to make two changes to AbstractEntityPersister:

1) Made GetGeneratedIdentity virtual/overridable

2) Modified the function Insert by extracting a method so it could be overriden. ie,

Code:
if (sql.CommandType == CommandType.Text)
{
          insertSelectSQL = Dialect.AddIdentitySelectToInsert(text, GetKeyColumns(0)[0], GetTableName(0));
}


became
Code:
if (sql.CommandType == CommandType.Text)
{
          insertSelectSQL = AddIdentitySelectToInsert(sql.Text);         
}


coupled with

Code:
    protected virtual SqlString AddIdentitySelectToInsert(SqlString text)
    {
      return Dialect.AddIdentitySelectToInsert(text, GetKeyColumns(0)[0], GetTableName(0));
    }


Here's the persister I use for tables that require it:
Code:
Imports NHibernate
Imports NHibernate.Persister.Entity
Imports NHibernate.Mapping
Imports NHibernate.Cache
Imports NHibernate.Engine


Namespace ORM.Extensions

   Public Class CompositeIdentityTablePersister
      Inherits NHibernate.Persister.Entity.SingleTableEntityPersister

      Public Sub New(ByVal model As PersistentClass, ByVal cache As ICacheConcurrencyStrategy, _
       ByVal factory As ISessionFactoryImplementor, ByVal mapping As IMapping)
         MyBase.New(model, cache, factory, mapping)
      End Sub

      Public Overrides ReadOnly Property IdentifierGenerator() As NHibernate.Id.IIdentifierGenerator
         Get
            Return New NHibernate.Id.IdentityGenerator
         End Get
      End Property

      Public Overrides ReadOnly Property IsIdentifierAssignedByInsert() As Boolean
         Get
            Return True
         End Get
      End Property

      ''' <remarks>
      ''' We override this so we can return a null identifier if the ID is zero, which allows IsUnsaved in the persister to
      ''' spot this record needs saving
      ''' </remarks>
      Public Overrides Function GetIdentifier(ByVal obj As Object) As Object
         Dim key As DBKey = MyBase.GetIdentifier(obj)
         If key Is Nothing OrElse key.ID = 0 Then Return Nothing
         Return key
      End Function

      ''' <remarks>
      ''' Had to modify the base class AbstractEntityPersister to make this overridable
      ''' </remarks>
      Protected Overrides Function GetGeneratedIdentity(ByVal obj As Object, ByVal session As NHibernate.Engine.ISessionImplementor, ByVal rs As System.Data.IDataReader) As Object
         Dim id As DBKey = New DBKey
         Try
            If Not rs.Read() Then Throw New HibernateException("The database returned no natively generated identity value")
            id.ID = rs(0)
            id.Prefix = rs(1)
         Finally
            rs.Close()
         End Try
         Return id
      End Function

      ''' <remarks>
      ''' Had to extract this method from the Protected Insert method of the AbstractEntityPersister
      ''' </remarks>
      Protected Overrides Function AddIdentitySelectToInsert(ByVal text As NHibernate.SqlCommand.SqlString) As NHibernate.SqlCommand.SqlString
         ' Need to get the field names for the Prefix and ID columns
         Dim IDField As String = If(IdentifierColumnNames(0).EndsWith("ID"), IdentifierColumnNames(0), IdentifierColumnNames(1))
         Dim PrefixField As String = If(IdentifierColumnNames(0).EndsWith("ID"), IdentifierColumnNames(1), IdentifierColumnNames(0))
         ' Also need to identify which parameter contains the passed in prefix
         ' We'll assume that the first three parts are "INSERT INTO ", tablename and a comma, and that each field is
         ' separated by a comma
         Dim ValPartIndex As Integer = 0, PrefixIndex As Integer = -1
         For partindex As Integer = 4 To text.Parts.Count - 1 Step 2
            If text.Parts(partindex) = ") VALUES (" Then
               ValPartIndex = partindex
               Exit For
            End If
         Next
         For partindex As Integer = 3 To ValPartIndex Step 2
            If text.Parts(partindex) = "Prefix" Then
               PrefixIndex = (partindex - 3) / 2
               Exit For
            End If
         Next
         Return text.Append(String.Format("; SELECT SCOPE_IDENTITY() AS {0}, @p{2} AS {1}", IDField, PrefixField, PrefixIndex))
      End Function

   End Class

End Namespace


and here's an example mapping:

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Domain" assembly="Saturn.DAO">
   <class name="Saturn.DAO.Domain.Client, Saturn.DAO" table="Clients" persister="Saturn.DAO.ORM.Extensions.CompositeIdentityTablePersister, Saturn.DAO">
<!-- Identity -->
    <composite-id name="ID" class="Saturn.DAO.Types.DBKey">
      <key-property name="Prefix"/>
      <key-property name="ID"/>
    </composite-id>


DBKey is a class that manages the Prefix/ID pair for me

Hope that's some help to you. The solution turned out to be fairly simple, but working my way through the nhibernate code to get to it was somewhat challenging.

Kev


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 8:04 am 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
goblin wrote:
Kev
if i change nhibernate source code wouldnt I have to expose my source code which is a proprietary software due GPL licensing.


Hmm, I'm not sure, now you mention it. Our application is entirely in-house, whether that makes any difference.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.