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.  [ 1 post ] 
Author Message
 Post subject: Mapping components (value types) and audit information
PostPosted: Wed Sep 17, 2008 6:33 am 
Regular
Regular

Joined: Mon Aug 29, 2005 3:07 pm
Posts: 77
I'd like to have some advice regarding mapping components (value types) and audit information.

I will explain a little bit in more detail:

First of all, it is necessary to know that each table in my database contains audit-trail columns. That is, each and every table contains a column 'CreatedAt' 'LastModifiedAt', 'CreatedBy' and 'LastModifiedBy' columns.
These columns need to be updated when appropriate. (That is, on every update the LastModifiedAt and LastModifiedBy columns need to be updated, on every insert, all four columns need to be given a value).

Now, what I have is a table 'Chapter' which is related to a table 'ChapterName'. So, in fact each Chapter can have one to multiple names. (This is necessary since I keep track of names in different languages).

The tables look like this:
Code:
CREATE TABLE [dbo].[Chapter](
   [ChapterId] [uniqueidentifier] NOT NULL,
   [ChapterCode] [varchar](25) NOT NULL,
   [CreationDate] [datetime] NOT NULL,
   [ModificationDate] [datetime] NOT NULL,
   [Version] [int] NOT NULL,
   [CreatedBy] [varchar](50) NOT NULL,
   [LastUpdatedBy] [varchar](50) NOT NULL,
CONSTRAINT [PK_Chapter] PRIMARY KEY 
(
   [ChapterId] ASC
)


CREATE TABLE [dbo].[ChapterName](
   [ChapterNameId] [uniqueidentifier] NOT NULL,
   [ChapterId] [uniqueidentifier] NOT NULL,
   [Name] [varchar](255) NOT NULL,
   [LanguageCode] [int] NOT NULL,
   [CreationDate] [datetime] NOT NULL,
   [ModificationDate] [datetime] NOT NULL,
   [Version] [int] NOT NULL,
   [CreatedBy] [varchar](50) NOT NULL,
   [LastUpdatedBy] [varchar](50) NOT NULL,
CONSTRAINT [PK_ChapterName] PRIMARY KEY
(
   [ChapterNameId] ASC
)


As I see it, in my domain model, the Chapter class is an entity and ChapterName should be regarded as a value type.

My classes look like this:
Code:
public class Chapter : AuditableEntity<Guid>
    {
        public string ChapterCode
        {
            get;
            set;
        }

        private IList<ChapterName> _names = new List<ChapterName> ();

        public ReadOnlyCollection<ChapterName> Names
        {
            get
            {
                return new List<ChapterName> (_names).AsReadOnly ();
            }
        }

        public string GetChapterNameInLanguage( LanguageCode language )
        {
            foreach( ChapterName name in _names )
            {
                if( name.Language == language )
                {
                    return name.Name;
                }
            }
            return string.Empty;
        }       
    }

    public class ChapterName : IAuditable
    {
        public LanguageCode Language
        {
            get;
            set;
        }

        public string Name
        {
            get;
            set;
        }

        public Chapter Chapter
        {
            get;
            internal set;
        }

        #region Equals / GetHashcode()

        public override bool Equals( object obj )
        {
            ChapterName other = obj as ChapterName;

            if( other == null )
            {
                return false;
            }

            return this.GetHashCode () == other.GetHashCode ();

        }
       
        public override int GetHashCode()
        {
            return ( "Language:" + Language + "|Name:" + Name ).GetHashCode ();
        }

        #endregion

        #region Auditable


        public DateTime Created
        {
            get;
            private set;
        }

        public string CreatedBy
        {
            get;
            private set;
        }

        public string CreatedByPropertyName
        {
            get
            {
                return "CreatedBy";
            }
        }

        public string CreatedPropertyName
        {
            get
            {
                return "Created";
            }
        }

        public string LastUpdatedBy
        {
            get;
            private set;
        }

        public string LastUpdatedByPropertyName
        {
            get
            {
                return "LastUpdatedBy";
            }
        }

        void IAuditable.SetCreatedBy( string createdBy )
        {
            CreatedBy = createdBy;
        }

        void IAuditable.SetCreationDate( DateTime created )
        {
            Created = created;
        }

        void IAuditable.SetLastUpdatedBy( string lastUpdatedBy )
        {
            LastUpdatedBy = lastUpdatedBy;
        }

        void IAuditable.SetUpdateDate( DateTime updated )
        {
            Updated = updated;
        }

        public DateTime Updated
        {
            get;
            private set;
        }

        public string UpdatedPropertyName
        {
            get
            {
                return "Updated";
            }
        }

        public int Version
        {
            get;
            private set;
        }

        #endregion
    }
The AuditableEntity base class contains some basic logic / fields (Id, the Audit properties, Equals & Gethashcode overrides).

Now, the question is, how do I map this situation best ? As you can see, the ChapterName table has a surrogate primary key as well (ChapterNameId).
Therefore, I think i should map this using an id-bag.

My mapping looks like this:
Code:
<class name="Chapter" table="Chapter" lazy="false">

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

    <version name="Version" column="Version" />

    <property name="ChapterCode" column="ChapterCode" />

    <idbag name="Names" access="field.camelcase-underscore" lazy="false" table="ChapterName">

      <collection-id column="ChapterNameId" type="guid">
        <generator class="guid.comb" />
      </collection-id>
     
      <key column="ChapterId" />
     
      <composite-element class="ChapterName">

        <parent name="Chapter" />
     
        <property name="Language" column="LanguageCode" />
        <property name="Name" column="Name"/>

        <property name="Created" column="CreationDate" />
        <property name="Updated" column="ModificationDate" />
        <property name="CreatedBy" column="CreatedBy" />
        <property name="LastUpdatedBy" column="LastUpdatedBy" />       
      </composite-element>
   
    </idbag>

   
    <property name="Created" column="CreationDate" />
    <property name="Updated" column="ModificationDate" />
    <property name="CreatedBy" column="CreatedBy" />
    <property name="LastUpdatedBy" column="LastUpdatedBy" />
   
  </class>


As you can see, I've mapped the Chapter entity to the Chapter table, and the ChapterNames collection as a 'component collection' using an idbag. (I've used an idbag so that the primary key column in the ChapterName table can be given a value.

To update the audit-information, i've created an AuditInterceptor which I use in my Session. This works ok for the Chapter class. However, when I add a ChapterName to the Chapter, it seems that the interceptor doesn't intercept the chaptername components.

Now, I have 2 questions:
- what do you think about my mappings ? Is the mapping that i've showed, ok ?
- how can I make sure that the audit information is correctly set for the ChapterNames as well ? I can solve it when I map the ChapterName class as an entity, but this is maybe not a good practice ?


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

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.