摘要:写这篇文章缘于昨天跟Linkin的一段聊天。我在使用ActiveRecord的一些技巧一文中的由实体类生成数据库表提到了这样一句话:生成数据库表时只有当该表不存在时ActiveRecord才会生成,否则表如果存在ActiveRecord不会做任何事情,也不会报任何错误。Linkin说他在实验时如果数据库表存在,ActiveRecord会删除表中的记录,其实这句话是在有些情况下是不对的,本篇文章将详细介绍Castle ActiveRecord中的Schema Pitfals。
主要内容
1.引言
2.CreateSchema和DropSchema
3.CreateSchemaFromFile
4.GenerateCreationScripts和GenerateDropScripts
一.引言
我在Castle ActiveRecord学习实践(9):使用ActiveRecord的一些技巧一文中的由实体类生成数据库表提到了这样一句话:生成数据库表时只有当该表不存在时ActiveRecord才会生成,否则表如果存在ActiveRecord不会做任何事情,也不会报任何错误。Linkin说他在实验时如果数据库表存在,ActiveRecord会删除表中的记录,其实那句话是在有些情况下是不对的,通过后面的分析我们会看到。
Castle ActiveRecord为我们提供了由实体类生成数据库表的方法,它其实在底层是封装了NHibernate.Tool.hbm2ddl中的SchemaExport,既创建数据库表的方法都是通过SchemaExport类来完成了,所有的这些方法都在ActiveRecordStarter中提供,列表如下:
方 法 | 示 例 |
CreateSchema() | ActiveRecordStarter.CreateSchema(); |
CreateSchemaFromFile() | ActiveRecordStarter.CreateSchemaFromFile("blog.sql"); |
DropSchema () | ActiveRecordStarter.DropSchema(); |
GenerateDropScripts() | ActiveRecordStarter.GenerateDropScripts("blog.sql"); |
GenerateCreationScripts() | ActiveRecordStarter.GenerateCreationScripts("blog.sql"); |
二.CreateSchema和DropSchema
CreateSchema根据实体类来生成数据库表,在调用ActiveRecordStarter.CreateSchema()之后,我们来看一下ActiveRecord中执行了什么操作:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
可以看到在ActiveRecord中,仅仅是调用了NHibernate中的SchemaExport的Create方法,并传递了两个参数分别为fasle,true。现在我们跟踪代码到NHibernate中:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
其中的Execute实现如下,为了简单起见,我省略了部分代码:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/dot.gif)
![](http://terrylee.cnblogs.com/Images/dot.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/dot.gif)
![](http://terrylee.cnblogs.com/Images/dot.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
从代码中我们可以看到,不管传入的参数如何,它都会执行删除脚本,然后判断,是否只删除而不创建,这样一来,用CreateSchema来生成数据库表,如果表已经存在,它删除了已有的记录就是必然的了,也就是说执行这个方法,相当于执行了下面这样一段SQL脚本:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
同样DropSchema也是这样,不过justDrop为True罢了,它其实就执行了下面这句话:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
这也就是说, “生成数据库表时只有当该表不存在时ActiveRecord才会生成,否则表如果存在ActiveRecord不会做任何事情,也不会报任何错误”在这种情况下是不对的,ActiveRecord会删除已经存在的数据库表,并重新创建,这就会导致表中的数据丢失。
三.CreateSchemaFromFile
CreateSchemaFromFile方法用来执行一段已经存在的SQL脚本,基本用法如下:
ActiveRecordStarter.CreateSchemaFromFile("blog.sql")
与其它四个方法不同的是这个方法并没有使用NHibernate的任何操作,而是通过Castle ActiveRecord自己的ARSchemaCreator来实现的。
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
在ARSchemaCreator中,它会对SQL脚本进行分析,并通过IDbConnection,IDbCommand实现,看其中的一个方法:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
创建数据库表的方法很简单,但是我们知道在查询分析器中,如果执行创建数据库表的SQL脚本,这张表存在的话,它会报“数据库中已存在名为 '' 的对象”错误,那我们看上面这段代码catch块中有这样一句注释和代码:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
这就是说如果数据库中存在有Blogs这张表,我们再执行再通过CreateSchemaFromFile()方法来执行下面这段脚本,ActiveRecord将不做任何事情,并且不会报错:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
所以,前面那句话,应该是在使用CreateSchemaFromFile()方法的情况有效。
四.GenerateCreationScripts和GenerateDropScripts
有时候,我们可以使用这两个方法来生成创建或者删除数据库表的SQL脚本,然后再利用CreateSchemaFromFile()使用这些脚本。这两个方法的使用很简单:
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://terrylee.cnblogs.com/Images/OutliningIndicators/None.gif)
它也是调用了NHibernate中的相应的方法,将会在当前应用程序目录下生成一个blog.sql的脚本文件。
这篇文章就分析到这儿,最后特别要感谢Linkin,没有他提的问题,我也不会去深入的研究这其中的细节,在以后的文章中,我会更加认真的去对待每一个问题。
参考资料
Castle的官方网站http://www.castleproject.org
作者: TerryLee出处: http://terrylee.cnblogs.com