PDA

View Full Version : Replace or Refresh the ISessionFactory


Chris Coleman
05-16-2007, 12:01 PM
Hi,

I was wondering if the suggestion at the end of this thread were ever implemented?

http://forum.springframework.net/showthread.php?t=232&highlight=Configuration+reload

I would like to replace the existing session factory with a new one, that contains an updated configuration for one of my database entities.

If it has not been implemented, how would I go about resetting the ISessionFactory instance used by all of the HibernateTemplate's?

Thanks
Chris

Chris Coleman
05-18-2007, 04:51 PM
Hi, I have implemented this suggestion from the above thread:

To address your use case, you could write an IFactoryObject implementation that either returns current ISessionFactory or disposes current one and creates a new one when Hibernate configuration object changes.

For example, your configuration object could have ChangeDatabase method that would load new configuration and raise DatabaseChanged event. HibernateSessionFactory would implement IFactoryObject interface and listen for DatabaseChanged event. When the event is raised, it would clean up/dispose existing ISessionFactory and obtain a new one from the configuration object, which would now become current session factory.

This way you get a "singleton-that-changes-from-time-to-time" :-) Oh yeah, you also need to make sure that HibernateSessionFactory's IsSingleton property returns false, to make it look like a prototype to other objects.

My HibernateSessionFactory is basically a modified copy of the LocalSessionFactoryObject. When the event fires it is refreshing correctly and generating a new session factory based on the new config and disposing of the old one.

However I'm struggling to see how to make this new SessionFactory hook into the SessionFactory that is used by the HibernateTemplate of the DAO's.

As I understand it (which is probably not too well) then the HibernateSessionFactory object's GetObject() method returns the actual session factory produced by the call config.BuildSessionFactory(). This is set on each of my DAO's when they are first initalised, but when the session factory is updated, the new session factory (produced by config.BuildSessionFactory() ) is not then set in the DAO's.

Any pointers as to where I'm going wrong with this would be great,

Cheers
Chris

Chris Coleman
05-18-2007, 07:11 PM
Ok,

Further digging around... I think the "singleton-that-changes-from-time-to-time" concept isn't quite working. There are only as many calls to my session factory's GetObject() method as there are objects that have a reference to the actual session factory. (HibernateTransactionManager, and my DAO's).

Ideally I'd like these objects to re-obtain the actual session factory used each time they need it, by calling GetObject() again, or via some other trickery, thus picking up the newly configured instance.

Thanks
Chris

Chris Coleman
05-18-2007, 10:14 PM
More replying to myself... ;)

One solution I have come up with is to have my SessionFactoryObject raise an event when the underlying configuration has been changed - this periodically check to see if the mapping configuration has changed and then if needs be updates. This event is then subscribed to by all the DAO's and an extended HibernateTransactionManager and handled as below:

The HibernateFactoryObject is passed in via constructor injection, using the &SessionFactory syntax to get the actual object instead of the GetObject() version.


public class ExtendedHibernateTransactionManager
: HibernateTransactionManager
{
public ExtendedHibernateTransactionManager(HibernateFacto ryObject obj)
: base()
{
obj.FactoryChanged += new EventHandler(OnChanged);
}

public void OnChanged(object sender, EventArgs e)
{
IFactoryObject fact = sender as IFactoryObject;
SessionFactory = fact.GetObject() as ISessionFactory;
}
}


The DAO's also subscribe to this event, and handle it in the same way.

I think this then results in all the session factories being updated.

So long as this all occurs while there are no operations being executed, and no transactions running then I think this should work.

Having said that - can anyone see any gotchas that could crop up with this. Say long running sessions that are re-attached, or something that I may not have considered?

Or am I just making things overly complex and missing the obvious?

Thanks
Chris

Erich Eichinger
05-21-2007, 02:00 AM
Hi,

instead of having all DAOs + HibernateTransactionManager subscribe to the change event, you could try to implement ISessionFactory in a ChangeableSessionFactory, that wraps the "real" SessionFactory and let your SessionFactoryObject hand out such a ChangeableSessionFactory instance.


class ChangeableSessionFactory : ISessionFactory
{
private ISessionFactory currentSessionFactory;

public ChangeableSessionFactory( ChangeableSessionFactoryObject factoryObject )
{
factoryObject.Changed += new EventHandler( ConfigurationChanged );
currentSessionFactory = factoryObject.GetSessionFactory();
}

private void ConfigurationChanged( object sender, EventArgs ea )
{
currentSessionFactory = ((ChangeableSessionFactoryObject)sender).GetSessio nFactory();
}

// implementation of ISessionFactory delegates calls
// to underlying currentSessionFactory
}

class ChangeableSessionFactoryObject : LocalSessionFactoryObject
{
public event EventHandler Changed;

// return wrapped session factory
override object GetObject()
{
return new ChangeableSessionFactory( this );
}

// return real session factory
ISessionFactory GetSessionFactory()
{
return base.GetObject();
}

...
}


cheers,
Erich

Chris Coleman
05-21-2007, 09:34 AM
Hi,

Thanks for the reply.

It seems like a good solution and is definitely neater than the one I proposed above, however I am stuck with one problem:

object IFactoryObject.GetObject()

is not marked as virtual so I can not override it, meaning that the base's definition of get object is always being called, instead of the code to create a new ChangeableSessionFactory instead of the basic session factory.

Do you have any thoughts on a way around this? It would be good to get this working since it is a much cleaner way of achieving this.

Cheers
Chris

Erich Eichinger
05-21-2007, 09:40 AM
Hi Chris,

You can always get around such a problem by explicitely implementing the interface in your derived class.

cheers,
Erich

Chris Coleman
05-21-2007, 12:15 PM
Of course - apologies for that...

Everything appears fine with this now, except for one warning in the logs:

WARN Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Using FallbackException Translator. Could not translate from ISessionFactory to IDbProvider

I solved this by implenting the ISessionFactoryImplementor interface on ChangeableSessionFactory instead of ISessionFactory.

Many thanks for the help,
Chris

Erich Eichinger
05-21-2007, 12:49 PM
Hi Chris,

Glad you finally made it - After getting this to work it would be great if you could donate your code to the Spring.Data.NHibernate integration project? I'm sure other people would appreciate it ;-)

cheers,
Erich

Chris Coleman
05-21-2007, 01:45 PM
Of course :)

Where would be the best place to send it?

Erich Eichinger
05-21-2007, 02:43 PM
Hi,

I added a Jira issue SNH-13 (http://opensource.atlassian.com/projects/spring/browse/SNH-13) to the NHibernate integration project. If you don't have a Jira account, please get one and attach your code to this issue.

tx a lot,
Erich