今天早上看到一篇好文<使用NHibernate进行数据持久层开发的最佳实践>
个人认为写得很好,唯其不足就是过于抽象
实际上这篇文章就是理清了使用NHIBERNATE后开发持久层的一个顺序,当然,这个不是唯一,但是,如果是一个新项目,那确实是一个"最佳实践"
写好HBML.XML文件->使用Nhibernate.Tool.Hbm2Net生成POJO对象-->同时使用Hbm2DDL来导出或直接在数据库中生成数据表,这样可以减轻不少负担
需要注意的是Hbm2DDL功能是包含在NHIBERNATE中的,这个部分如何使用,可以参见NHIBERNATE的EXAMPLE和TEST项目,里面的例子非常棒
但是NHBIERNATE.TOOL.HBM2NET就不一样了,它是NHibernateContrib的一部分,所以,你必须下载NhibernateContrib才能使用
一开始,我使用以前下载的NHIBERNATE1.0.1版本和NhibernateContrib1.0.1版,使用NANT编译NhibernateContrib发生错误,于是用VS2003打开解决方案,用VS来编译(VS能提供更为详尽的错误信息),错误一大堆,首先的一个错误发生在
F:\SoftWare\DotNet\Persiste\NHibernateContrib-1.0.2.0\src\Nullables.Tests\NullableCharFixture.cs(92):   Assert.IsTrue(ParseToStringValue('?));
一眼看出,少了个单引号(真奇怪,难道开发人员没有成功编译就交出来吗?或者他们在交出来的时候搞错了这个文件的版本,又或者这根本是CONTRIB开发人员给我们开的一个小小的玩笑)
接下来有几条错误都是与SNK密钥文件有关,直接打开各个项目的AssemblyInfo.cs文件,把与SNK文件有关的行删掉就行了
把这些都修正后,重新编译,OK

接下来,试一试Nhibernate.tool.hbm2net.exe啦
如果是nant编译,你就进入build文件夹,一层一层找到nhibernate.tool.hbm2net.exe
这是一个命令行工具,这里,我要批评一下开发人员了,既然提供了这么一个工具,为什么不把它稍稍完美一下呢,就算命令行工具,当使用人员不输入任何参数时也要给出一个使用说明以及参数含义列表吧,可是nhibernate.tool.hbm2net竟然没有,没有法子,打开Nhibernate.tool.hbm2net项目查看,在CODEGenerator.CS文件中发现了它对参数的判断(够晕的)
for (int i = 0; i < args.Length; i++)
                
{
                    
if (args[i].StartsWith("--"))
                    
{
                        
                        
if (args[i].StartsWith("--config="))
                        
{
                            
// parse config xml file
                            Document document = new XmlDocument();
                            document.Load(args[i].Substring(
9));
                            globalMetas 
= MetaAttributeHelper.loadAndMergeMetaMap((document["codegen"]), null);
                            IEnumerator generateElements 
= document["codegen"].SelectNodes("generate").GetEnumerator();
                            
                            
while (generateElements.MoveNext())
                            
{
                                generators.Add(
new Generator((Element) generateElements.Current));
                            }

                        }

                        
else if (args[i].StartsWith("--output="))
                        
{
                            outputDir 
= args[i].Substring(9);
                        }

                    }

                    
else
                    
if ( args[i].IndexOf("*"> -1 )
                    
{
                        
// Handle wildcards
                        mappingFiles.AddRange( GetFiles( args[ i ] ) );
                    }

                    
else
                    
{
                        mappingFiles.Add(args[i]);
                    }

可以看出来这个命令行有一个以上参数,其中两个是可选的,使用开式如下:
Nhibernate.tool.hbm2net Hbm文件1 [hbm文件2]  [hbm文件3]...[hbm文件n] [--config=config文件][--output=输出目录]
所以,一般来说,你只要这样用就成了
Nhibernate.tool.hbm2net blogitem.hbm.xml
nhibernate.tool.hbm2net *.hbml.xml --output=c:\temp\prj1
nhibernate.tool.hbm2net blogitem.hbm.xml blog.hbm.xml

看到这里,如果你以为OK了,那你就错了,虽然编译成功了,但是我注意到VS2003有两条提示是关于Log4net版本问题的
警告: 不能将项目“NHibernate.Tasks-1.1”中的依赖项“log4net, Version=1.2.1.40796, Culture=neutral, PublicKeyToken=aaa0f432407c68df”复制到运行目录,因为它将改写引用“log4net, Version=1.2.9.0, Culture=neutral, PublicKeyToken=b32731d11ce58905”
运行的时候,会出现LOG4NET的错误提示而无法继续执行(这点也看出来,开发人员处理的不好,至少LOG4NET出现问题是不会影响到CONTRIB主体功能的,应该进行异常处理并提示用户是否继续为妥吧)
进行到这里,发现不行,于是,在每一个CONTRIB项目文件中找到LOG4NET的引用,看是否有引用特定版本的地方,发现每一个文件都引用的是LOG4NET 1.2.9,根本没有用引1.2.1的地方,郁闷,难道就此放弃?
在GOOGLE上搜了一下类似错误,找到一位同学说
"如果A项目中引用了c.dll,版本是1.1而在B项目中同时引用了A项目和c.dll的1.2版,VS就会提示上面的错误信息"
比对一下,意思很明显,就是CONTRIB所引用的DLL中,有某个DLL引用了LOG4NET,但是,它是引用的1.2.1版本,所以导致此错误信息
经查是NVelocity.dll这个DLL引用了LOG4NET 1.2.1版,但是也没办法呀
于是,去NHIBERNATE的老家,刚好看到它新出来1.0.2版,于是,下载
重新编译,发现1.0.2中竟然也有"少引号"这么低级的错误,于是,像上面一样,逐个修正
更好笑的是1.0.2的BUG更多一些,Hbm2NET和Task项目中引用了LIB文件夹(就是说,有引用某DLL,VS就在这个文件夹中找),但是,我打开LIB文件夹,却没有NVelocity.dll文件(晕了吧),跟1.0.1版比,LIB文件夹少了好几个文件
想想办法,移花接木,就把1.0.1下的nunit.core.dll和NVelocity.dll等几个相关的DLL复制到LIB文件夹下,再编译,终于通过,用NANT编译一下(NANT编译完的很干净,直接在BUILD文件夹下,不用到处找)
这回,打开命令行提示窗,复制一个NHIBERNATE例子中的HBM.XML文件,BLOGITEM.HBM.XML,输入:nhibernate.tool.hbm2net blogitem.hbm.xml
COMMAND空显示:"nhibernate.tool.hbm2net"既不是命令,也没有此文件
我再晕
用DIR一列,噢,原来1.0.2把它改名了,现在叫nhibernate.tool.hbm2net.console.exe
输入nhibernate.tool.hbm2net.console blogitem.hbm.xml,顺利执行
执行完后,多了个Generated文件夹,一层层进去generated/nhibernate/examples/blogger/blog,nhibernate,终于看到一个文件examples.cs(我倒,难道不会跟HBM文件的主文件名相同吗?还有,目录级次太深了吧,生成的命名空间也是深不见底呀)
大家有兴趣的可以试试,不管怎么说,我觉得这是一个不错的主意,只是,tool2net工具写的太次了,有时间的话,哪位高手哥哥可以修正一下它的BUG,改的更好点,最好有个GUI,那就棒了

另外,HBM文件,写起来也很容易,你只要复制NHIBERNATE里的两个XSD到C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml就行了,另外,如果你实在觉得手工写太累,OK,去http://www.ayende.com/下载个Nhibernate Query Analyzer就可以了,它是一个GUI的NHIBERNATE配置及映射文件编辑器,而且也是一个HQL的执行容器

补充,再次看了一下NhibernteContrib的代码,彻底搞清了它的用法:
nhibernate.tool.hbm2net.console [hbm.xml文件列表|path\*.hbm.xml] --output 输出文件夹 --config hbm2net的配置文件
不过,需要注意的是以下几点:
1,如果执行该程序时,不在其BIN文件夹的话(就是CONFIG文件跟EXE文件没在一起的话,必须指定CONFIG选项),这时程序执行,没有任何错误提示,但是,没有成功
2,如果使用通配符法的时候,一定要使用完整的WINDOWS路径,eg.C:\temp\*.hbml.xml,如果hbm.xml文件跟nhibernate.tool.hbm2net.console在一个文件夹时,也不能写成nhibernate.tool.hbm2net.console *.hbm.xml,因为在程序中,它是以最后一个反斜杠(\)作为路径的截取字符的,如果你不写出这个\来,程序一样无法执行(有点变态吧),所以,正确的写法是:nhibernate.tool.hbm2net.console .\*.hbm.xml即可
3.如果不写output选项,则输出会在当前文件夹.会生成一个generated文件夹,然后就是取映射文档中指定的CLASS名称中的程序集名的最后一段,生成子文件夹,再取CLASS名称中类名的NAMESPACE中最后一段,生成三级文件夹,最后,才是取类名,生成形如"类名,命名空间"的四级文件夹,最终生成的类文件就在里面了.(不得不说,这个主意考虑到了命名空间的层级问题,可是,用逗号做文件夹名称中的一部分,实在不是个好主意,还有,我们经常需要的是生成平坦的,就是HBM.XML和类文件夹在一级的方式)
如果你想把结果放在别处,可以加上--output directory就可以了,不过,文件夹结构是一样的