Castle ActiveRecord学习实践(10):深度分析Schema Pitfals

简介:



摘要:写这篇文章缘于昨天跟Linkin的一段聊天。我在使用ActiveRecord的一些技巧一文中的由实体类生成数据库表提到了这样一句话:生成数据库表时只有当该表不存在时ActiveRecord才会生成,否则表如果存在ActiveRecord不会做任何事情,也不会报任何错误。Linkin说他在实验时如果数据库表存在,ActiveRecord会删除表中的记录,其实这句话是在有些情况下是不对的,本篇文章将详细介绍Castle ActiveRecord中的Schema Pitfals

 
主要内容
1 .引言
2 CreateSchemaDropSchema
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中执行了什么操作:
None.gif public   static   void  CreateSchema()
ExpandedBlockStart.gif
{
InBlock.gif    CheckInitialized();
InBlock.gif
InBlock.gif    
foreach(Configuration config in ActiveRecordBase._holder.GetAllConfigurations())
InBlock.gif
ExpandedSubBlockStart.gif    
{
InBlock.gif        SchemaExport export 
= CreateSchemaExport(config);
InBlock.gif
InBlock.gif        
try
ExpandedSubBlockStart.gif        
{
InBlock.gif            export.Create( 
falsetrue );
ExpandedSubBlockEnd.gif        }

InBlock.gif        
catch(Exception ex)
ExpandedSubBlockStart.gif        
{
InBlock.gif            
throw new ActiveRecordException( "Could not create the schema", ex );
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}
可以看到在 ActiveRecord中,仅仅是调用了 NHibernate中的 SchemaExportCreate方法,并传递了两个参数分别为 fasletrue。现在我们跟踪代码到 NHibernate中:
None.gif public   void  Create(  bool  script,  bool  export )
ExpandedBlockStart.gif
{
InBlock.gif    Execute( script, export, 
falsetrue );
ExpandedBlockEnd.gif}
其中的 Execute实现如下,为了简单起见,我省略了部分代码:
None.gif public   void  Execute(  bool  script,  bool  export,  bool  justDrop,  bool  format )
ExpandedBlockStart.gif
{
InBlock.gif    IDbConnection connection 
= null;
InBlock.gif
InBlock.gif    StreamWriter fileOutput 
= null;
InBlock.gif
InBlock.gif    IConnectionProvider connectionProvider 
= null;
InBlock.gif
InBlock.gif    IDbCommand statement 
= null;
InBlock.gif
InBlock.gif    
//dot.gifdot.gif
InBlock.gif

InBlock.gif    
try
ExpandedSubBlockStart.gif    
{
InBlock.gif        
if( outputFile != null )
ExpandedSubBlockStart.gif        
{
InBlock.gif            fileOutput 
= new StreamWriter( outputFile );
InBlock.gif
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
if( export )
ExpandedSubBlockStart.gif        
{
InBlock.gif
InBlock.gif            connectionProvider 
= ConnectionProviderFactory.NewConnectionProvider( props );
InBlock.gif
InBlock.gif            connection 
= connectionProvider.GetConnection();
InBlock.gif
InBlock.gif            statement 
= connection.CreateCommand();
InBlock.gif
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
//格式化删除SQL脚本
InBlock.gif
InBlock.gif         
//执行脚本
InBlock.gif

InBlock.gif        
if!justDrop )
ExpandedSubBlockStart.gif        
{
InBlock.gif            
//格式化创建SQL脚本
InBlock.gif
InBlock.gif            
//执行脚本
InBlock.gif

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif    
catch( HibernateException )
ExpandedSubBlockStart.gif    
{
InBlock.gif        
throw;
ExpandedSubBlockEnd.gif    }

InBlock.gif    
finally
ExpandedSubBlockStart.gif    
{
InBlock.gif        
//dot.gifdot.gif.
ExpandedSubBlockEnd.gif
    }

ExpandedBlockEnd.gif}
从代码中我们可以看到,不管传入的参数如何,它都会执行删除脚本,然后判断,是否只删除而不创建,这样一来,用 CreateSchema来生成数据库表,如果表已经存在,它删除了已有的记录就是必然的了,也就是说执行这个方法,相当于执行了下面这样一段 SQL脚本:
None.gif if   exists  ( select   *   from  dbo.sysobjects  where  id  =   object_id (N ' Blogs ' and   OBJECTPROPERTY (id, N ' IsUserTable ' =   1
None.gif
None.gif
drop   table  Blogs
None.gif
None.gif
create   table  Blogs (
None.gif
None.gif  blog_id 
INT   IDENTITY   NOT   NULL ,
None.gif
None.gif   blog_name 
NVARCHAR ( 255 null ,
None.gif
None.gif   blog_author 
NVARCHAR ( 255 null ,
None.gif
None.gif   
primary   key  (blog_id)
None.gif
None.gif)
同样 DropSchema也是这样,不过 justDropTrue罢了,它其实就执行了下面这句话:
None.gif if   exists  ( select   *   from  dbo.sysobjects  where  id  =   object_id (N ' Blogs ' and   OBJECTPROPERTY (id, N ' IsUserTable ' =   1
None.gif
None.gif
drop   table  Blogs
这也就是说, “生成数据库表时只有当该表不存在时 ActiveRecord才会生成,否则表如果存在 ActiveRecord不会做任何事情,也不会报任何错误”在这种情况下是不对的, ActiveRecord会删除已经存在的数据库表,并重新创建,这就会导致表中的数据丢失。
三. CreateSchemaFromFile
CreateSchemaFromFile 方法用来执行一段已经存在的SQL脚本,基本用法如下:
ActiveRecordStarter.CreateSchemaFromFile("blog.sql")
与其它四个方法不同的是这个方法并没有使用NHibernate的任何操作,而是通过Castle ActiveRecord自己的ARSchemaCreator来实现的。
None.gif public   static   void  CreateSchemaFromFile(String scriptFileName)
ExpandedBlockStart.gif
{
InBlock.gif    CheckInitialized();
InBlock.gif
InBlock.gif    ARSchemaCreator arschema 
= new ARSchemaCreator( 
InBlock.gif        ActiveRecordBase._holder.GetConfiguration( 
typeof(ActiveRecordBase) ) );
InBlock.gif
InBlock.gif    arschema.Execute( scriptFileName );
InBlock.gif
ExpandedBlockEnd.gif}
ARSchemaCreator 中,它会对SQL脚本进行分析,并通过IDbConnectionIDbCommand实现,看其中的一个方法:
None.gif public   static   void  ExecuteScriptParts(IDbConnection connection, String[] parts)
ExpandedBlockStart.gif
{
InBlock.gif    
using(IDbCommand statement = connection.CreateCommand())
ExpandedSubBlockStart.gif    
{
InBlock.gif        
foreach(String part in parts)
ExpandedSubBlockStart.gif        
{
InBlock.gif            
try
ExpandedSubBlockStart.gif            
{
InBlock.gif                statement.CommandText 
= part;
InBlock.gif
InBlock.gif                statement.CommandType 
= CommandType.Text;
InBlock.gif
InBlock.gif                statement.ExecuteNonQuery();
ExpandedSubBlockEnd.gif            }

InBlock.gif            
catch(Exception ex)
ExpandedSubBlockStart.gif            
{
InBlock.gif                
// Ignored, but we output it
InBlock.gif

InBlock.gif                Debug.WriteLine(String.Format(
"SQL: {0} \r\nthrew {1}. Ignoring", part, ex.Message));
InBlock.gif
ExpandedSubBlockEnd.gif            }

InBlock.gif
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}
创建数据库表的方法很简单,但是我们知道在查询分析器中,如果执行创建数据库表的 SQL脚本,这张表存在的话,它会报“数据库中已存在名为  '' 的对象”错误,那我们看上面这段代码 catch块中有这样一句注释和代码:
None.gif //  Ignored, but we output it
None.gif

None.gifDebug.WriteLine(String.Format(
" SQL: {0} \r\nthrew {1}. Ignoring " , part, ex.Message));
这就是说如果数据库中存在有Blogs这张表,我们再执行再通过CreateSchemaFromFile()方法来执行下面这段脚本,ActiveRecord将不做任何事情,并且不会报错:
None.gif create   table  Blogs (
None.gif
None.gif  blog_id 
INT   IDENTITY   NOT   NULL ,
None.gif
None.gif   blog_name 
NVARCHAR ( 255 null ,
None.gif
None.gif   blog_author 
NVARCHAR ( 255 null ,
None.gif
None.gif   
primary   key  (blog_id)
None.gif
None.gif)
所以,前面那句话,应该是在使用 CreateSchemaFromFile()方法的情况有效。
四. GenerateCreationScripts GenerateDropScripts
有时候,我们可以使用这两个方法来生成创建或者删除数据库表的SQL脚本,然后再利用 CreateSchemaFromFile() 使用这些脚本。这两个方法的使用很简单:
None.gif ActiveRecordStarter.GenerateDropScripts( " blog.sql " );
None.gif
None.gifActiveRecordStarter.GenerateCreationScripts(
" blog.sql " );
它也是调用了 NHibernate中的相应的方法,将会在当前应用程序目录下生成一个 blog.sql的脚本文件。
 
这篇文章就分析到这儿,最后特别要感谢Linkin,没有他提的问题,我也不会去深入的研究这其中的细节,在以后的文章中,我会更加认真的去对待每一个问题。



















本文转自lihuijun51CTO博客,原文链接: http://blog.51cto.com/terrylee/67668  ,如需转载请自行联系原作者

相关文章
|
3月前
|
BI
123. SAP ABAP 显式增强技术之 New BAdI 的技术原理介绍
123. SAP ABAP 显式增强技术之 New BAdI 的技术原理介绍
28 1
|
3月前
|
BI
SAP ABAP 显式增强技术之 New BAdI 的技术原理介绍试读版
SAP ABAP 显式增强技术之 New BAdI 的技术原理介绍试读版
22 0
|
3月前
|
BI
SAP ABAP 显式增强技术之 New BAdI 的实战介绍 - 如何创建和激活增强实现试读版
SAP ABAP 显式增强技术之 New BAdI 的实战介绍 - 如何创建和激活增强实现试读版
19 0
|
3月前
|
BI
124. SAP ABAP 显式增强技术之 New BAdI 的实战介绍 - 如何创建和激活增强实现
124. SAP ABAP 显式增强技术之 New BAdI 的实战介绍 - 如何创建和激活增强实现
24 0
|
10月前
|
存储 关系型数据库 MySQL
第六章 schema设计与管理
第六章 schema设计与管理
|
SQL NoSQL PHP
Thinkphp中模型的正确使用方式,ORM的思想概念
对象-关系映射(OBJECT-RELATIONAL MAPPING,简称ORM) 这是在面向对象编程发展过程中演变出来的一种思想、行为概念。 主要用途是:把对象模型表示的对象映射到基于sql的关系模型数据库结构中去。 当改变这个对象自身的属性或者调用该对象的方法时,相对应的是执行某些sql语句。 这样子编写代码的人员就可以更好地编写业务逻辑,而非重复地编写增删改查sql语句。
192 0