-->
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.  [ 3 posts ] 
Author Message
 Post subject: [.NET 2.0] Hibernate Session management and TransactionScope
PostPosted: Tue May 09, 2006 6:11 am 
Beginner
Beginner

Joined: Thu Oct 16, 2003 7:30 am
Posts: 21
Location: Brussels
The idea of having a Hibernate session per JTA transaction (and auto close on transaction commit) in the Java version has always appealed to me because of the simple programming model. See http://forum.hibernate.org/viewtopic.php?p=2207904 for a complete implementation that predates Hibernate 3.0.

Being new to .NET and NHibernate I was asking myself if something similar can be achieved with the System.Transactions namespace classes in .NET 2.0. As far as I can see the two main ingredients would be:

1. Maintain a Map in a static field of a HibernateUtil class that maps Transaction.Current.TransactionInformation.LocalIdentifier to the associated Hibernate session. An example of this can also be found in JTASessionContext.java in Hibernate 3.

2. If the current Transaction does not have a Hibernate session associated with it yet, create a new session, put it in the Map and enlist an implementation of IEnlistmentNotification through the VolatileEnlist method of Transaction with the enlistment option EnlistDuringPrepareRequired. In the Prepare method implementation of the IEnlistmentNotification interface the Hibernate session should be closed and the entry removed from the Map. This part has already been discussed near the end of the page http://pluralsight.com/wiki/default.aspx/Don/TransactionsAndSessionSynchronization.html.

A static method CurrentSession() on a HibernateUtil class would then always return the correct Hibernate Session, without the application developer having to worry about anything else (if not using IoC). Does this make sense?

Johan.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 7:50 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
The way it's done in Hibernate, and the way I intend to implement it in NHibernate, is to create a class implementing NHibernate.ITransaction interface. The class would then detect if there is a transaction going on, or if it should create a new one, and in general it will hide details from the user.


Top
 Profile  
 
 Post subject: some code
PostPosted: Wed May 10, 2006 2:30 am 
Beginner
Beginner

Joined: Thu Oct 16, 2003 7:30 am
Posts: 21
Location: Brussels
Here's a little proof of concept I did. I realize it's not production quality. All suggestions to make it so are more than welcome!

There are two classes (a usage example follows at the end):

Code:
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Transactions;

    using NHibernate;

    public class HibernateUtil
    {
        private Dictionary<string, ISession> map = new Dictionary<string, ISession>();

        internal static HibernateUtil instance = new HibernateUtil();

        private ISessionFactory sessionFactory;

        private HibernateUtil()
        {
        }

        public static ISessionFactory SessionFactory
        {
            set
            {
                instance.sessionFactory = value;
            }
        }

        public static ISession CurrentSession
        {
            get
            {
                return instance.BuildCurrentSession();
            }
        }

        private ISession BuildCurrentSession()
        {
            Transaction currentTx = Transaction.Current;
            if (currentTx == null)
            {
                throw new ApplicationException("No System.Transactions.Transaction present");
            }
            string txId = currentTx.TransactionInformation.LocalIdentifier;
            ISession session = null;
            if(!map.ContainsKey(txId))
            {
                session = sessionFactory.OpenSession();
                map[txId] = session;
                currentTx.EnlistVolatile(new SessionEnlistmentNotification(session, txId),
                    EnlistmentOptions.EnlistDuringPrepareRequired);
                Debug.WriteLine("Returning new session for TX "+txId);
            }
            else
            {
                session = map[txId];
                Debug.WriteLine("Returning existing session for TX " + txId);
            }
            return session;
        }

        internal void Remove(string transactionId)
        {
            Debug.WriteLine("Cleaning up TX " + transactionId);
            map.Remove(transactionId);
        }
    }
and
Code:
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Transactions;

    using NHibernate;

    class SessionEnlistmentNotification : IEnlistmentNotification
    {
        private ISession session;
        private string transactionId;

        public SessionEnlistmentNotification(ISession session, string transactionId)
        {
            this.session = session;
            this.transactionId = transactionId;
        }
       
        void IEnlistmentNotification.Commit(Enlistment enlistment)
        {
            Debug.WriteLine("In Commit...");
            enlistment.Done();
        }

        void IEnlistmentNotification.InDoubt(Enlistment enlistment)
        {
            Debug.WriteLine("In InDoubt...");
            enlistment.Done();
        }

        void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
        {
            Debug.WriteLine("In Prepare...");
            try
            {
                session.Flush();
                Close();
            }
            catch (Exception e)
            {
                preparingEnlistment.ForceRollback();
                throw e;
            }
            preparingEnlistment.Prepared();
        }

        void IEnlistmentNotification.Rollback(Enlistment enlistment)
        {
            Debug.WriteLine("In Rollback...");
            session.Clear();
            Close();
            enlistment.Done();
        }

        private void Close()
        {
            session.Close();
            HibernateUtil.instance.Remove(transactionId);
            this.session = null;
        }

    }


It can be used like this:
Code:
   public class NetworkDemo
   {

      [STAThread]
      public static void Main(string[] args)
      {
         // configure the configuration
         Configuration ds = new Configuration()
            .AddClass(typeof(Vertex))
                .AddClass(typeof(Edge));
            //build a session factory
         ISessionFactory sessions = ds.BuildSessionFactory();

         // inject the session factory into the HibernateUtil singleton (to be done once)
            HibernateUtil.SessionFactory = sessions;

            using (TransactionScope txScope = new TransactionScope(TransactionScopeOption.Required))
            {
                DoSomeBusiness();
                DoSomeOtherBusiness();
                txScope.Complete();
            }

      }

      
      public static void DoSomeBusiness()
      {
            ISession session = HibernateUtil.CurrentSession;
            Vertex v = new Vertex();
            v.Name = "test-vertex-9";
            session.Save(v);
      }
                               
                                public static void DoSomeOtherBusiness()
                                {
                                }
      
   }

Please note we don't do an explicit session flush in the business method, nor are there any calls to the Hibernate's ITransaction. This, of course, makes the code only usable within a TransactionScope.

Johan.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 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.