Saturday, September 12, 2009

NHibernate auto create database bug when using attribute for mapping

I am on vacation. However, I've just received a feedback email from a developer in Brazil on TinyERP (an open source project that I posted several months ago). He found that TinyERP framework can not create database automatically although he set the "create" value for hibernate.hbm2ddl.auto NHibernate properties in spring.xml config.
Actually, I haven't released any version of TinyERP in Codeplex. In the past, I created a sample database (not generate DB automatically from schema). Anyway, I need to check this issue.
After several minutes checking, I found the issue:

In TinyERP, I use NHibernate attribute on Business Entity for mapping. To use this, we have to override a LocalSessionFactoryObject to add assembly contains Business Entity classes into HbmSerializer. HbmSerializer only helps us to create Hibernate mapping config in stream. A very stupid thing is: the code to check and generate DB schema does not exist in parent classes of LocalSessionFactoryObject => Nothing happens when you set hibernate.hbm2ddl.auto="create".

The old code
public class LocalSessionFactoryObjectImp : LocalSessionFactoryObject
    {
        protected override void PostProcessConfiguration(NHibernate.Cfg.Configuration config)
        {
            base.PostProcessConfiguration(config);
            HbmSerializer.Default.Validate = true;
            IList modules = ApplicationConfiguration.GetInstance().Modules;
            foreach (Module module in modules)
            {
                Assembly assemblyInAppDomain = FindAssembly(module.Assembly);
                if (assemblyInAppDomain != null)
                {
                    config.AddInputStream(HbmSerializer.Default.Serialize(assemblyInAppDomain));
                }
            }
           
        }

The above code is a common code template to override LocalSessionFactoryObject - that we mostly see in many forums. The problem is: default SessionFactoryObject in NHibernate keeps the code to export DB schema. However, LocalSessionFactoryObject and its parent class do not implement any line of code for this. So, we have to add this feature manually if we want to override it.

This is the new code to fix this issue:
public class LocalSessionFactoryObjectImp : LocalSessionFactoryObject
    {
        protected override void PostProcessConfiguration(NHibernate.Cfg.Configuration config)
        {
            base.PostProcessConfiguration(config);
            HbmSerializer.Default.Validate = true;
            IList modules = ApplicationConfiguration.GetInstance().Modules;
            foreach (Module module in modules)
            {
                Assembly assemblyInAppDomain = FindAssembly(module.Assembly);
                if (assemblyInAppDomain != null)
                {
                    config.AddInputStream(HbmSerializer.Default.Serialize(assemblyInAppDomain));
                }
            }
            SchemaExport schemaExport = new SchemaExport(config);
            string autoCreateDBMode = config.GetProperty("hibernate.hbm2ddl.auto");
            if (!string.IsNullOrEmpty(autoCreateDBMode) && autoCreateDBMode == "create")
            {
                schemaExport.Create(true, true);
            }
        }
}

Hope it's useful in case you have same problem with using NHiberate attribute for mapping and auto creating DB schema.