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

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

 

主要内容

1.引言

2CreateSchemaDropSchema

3CreateSchemaFromFile

4GenerateCreationScriptsGenerateDropScripts

 

 

一.引言

我在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");

二.CreateSchemaDropSchema

CreateSchema根据实体类来生成数据库表,在调用ActiveRecordStarter.CreateSchema()之后,我们来看一下ActiveRecord中执行了什么操作:

public static void CreateSchema()
{
    CheckInitialized();

    
foreach(Configuration config in ActiveRecordBase._holder.GetAllConfigurations())

    
{
        SchemaExport export 
= CreateSchemaExport(config);

        
try
        
{
            export.Create( 
falsetrue );
        }

        
catch(Exception ex)
        
{
            
throw new ActiveRecordException( "Could not create the schema", ex );
        }

    }

}

可以看到在ActiveRecord中,仅仅是调用了NHibernate中的SchemaExportCreate方法,并传递了两个参数分别为fasletrue。现在我们跟踪代码到NHibernate中:

public void Create( bool script, bool export )
{
    Execute( script, export, 
falsetrue );
}

其中的Execute实现如下,为了简单起见,我省略了部分代码:

public void Execute( bool script, bool export, bool justDrop, bool format )
{
    IDbConnection connection 
= null;

    StreamWriter fileOutput 
= null;

    IConnectionProvider connectionProvider 
= null;

    IDbCommand statement 
= null;

    
//

    
try
    
{
        
if( outputFile != null )
        
{
            fileOutput 
= new StreamWriter( outputFile );

        }


        
if( export )
        
{

            connectionProvider 
= ConnectionProviderFactory.NewConnectionProvider( props );

            connection 
= connectionProvider.GetConnection();

            statement 
= connection.CreateCommand();

        }


        
//格式化删除SQL脚本

         
//执行脚本

        
if!justDrop )
        
{
            
//格式化创建SQL脚本

            
//执行脚本

        }

    }

    
catch( HibernateException )
    
{
        
throw;
    }

    
finally
    
{
        
//.
    }

}

从代码中我们可以看到,不管传入的参数如何,它都会执行删除脚本,然后判断,是否只删除而不创建,这样一来,用CreateSchema来生成数据库表,如果表已经存在,它删除了已有的记录就是必然的了,也就是说执行这个方法,相当于执行了下面这样一段SQL脚本:

if exists (select * from dbo.sysobjects where id = object_id(N'Blogs'and OBJECTPROPERTY(id, N'IsUserTable'= 1

drop table Blogs

create table Blogs (

  blog_id 
INT IDENTITY NOT NULL,

   blog_name 
NVARCHAR(255null,

   blog_author 
NVARCHAR(255null,

   
primary key (blog_id)

)

同样DropSchema也是这样,不过justDropTrue罢了,它其实就执行了下面这句话:

if exists (select * from dbo.sysobjects where id = object_id(N'Blogs'and OBJECTPROPERTY(id, N'IsUserTable'= 1

drop table Blogs

这也就是说, “生成数据库表时只有当该表不存在时ActiveRecord才会生成,否则表如果存在ActiveRecord不会做任何事情,也不会报任何错误”在这种情况下是不对的,ActiveRecord会删除已经存在的数据库表,并重新创建,这就会导致表中的数据丢失。

三.CreateSchemaFromFile

CreateSchemaFromFile方法用来执行一段已经存在的SQL脚本,基本用法如下:

ActiveRecordStarter.CreateSchemaFromFile("blog.sql")

与其它四个方法不同的是这个方法并没有使用NHibernate的任何操作,而是通过Castle ActiveRecord自己的ARSchemaCreator来实现的。

public static void CreateSchemaFromFile(String scriptFileName)
{
    CheckInitialized();

    ARSchemaCreator arschema 
= new ARSchemaCreator( 
        ActiveRecordBase._holder.GetConfiguration( 
typeof(ActiveRecordBase) ) );

    arschema.Execute( scriptFileName );

}

ARSchemaCreator中,它会对SQL脚本进行分析,并通过IDbConnectionIDbCommand实现,看其中的一个方法:

public static void ExecuteScriptParts(IDbConnection connection, String[] parts)
{
    
using(IDbCommand statement = connection.CreateCommand())
    
{
        
foreach(String part in parts)
        
{
            
try
            
{
                statement.CommandText 
= part;

                statement.CommandType 
= CommandType.Text;

                statement.ExecuteNonQuery();
            }

            
catch(Exception ex)
            
{
                
// Ignored, but we output it

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

            }


        }


    }

}

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

// Ignored, but we output it

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

这就是说如果数据库中存在有Blogs这张表,我们再执行再通过CreateSchemaFromFile()方法来执行下面这段脚本,ActiveRecord将不做任何事情,并且不会报错:

create table Blogs (

  blog_id 
INT IDENTITY NOT NULL,

   blog_name 
NVARCHAR(255null,

   blog_author 
NVARCHAR(255null,

   
primary key (blog_id)

)

所以,前面那句话,应该是在使用CreateSchemaFromFile()方法的情况有效。

四.GenerateCreationScriptsGenerateDropScripts

有时候,我们可以使用这两个方法来生成创建或者删除数据库表的SQL脚本,然后再利用CreateSchemaFromFile()使用这些脚本。这两个方法的使用很简单:

ActiveRecordStarter.GenerateDropScripts("blog.sql");

ActiveRecordStarter.GenerateCreationScripts(
"blog.sql");

它也是调用了NHibernate中的相应的方法,将会在当前应用程序目录下生成一个blog.sql的脚本文件。

 

这篇文章就分析到这儿,最后特别要感谢Linkin,没有他提的问题,我也不会去深入的研究这其中的细节,在以后的文章中,我会更加认真的去对待每一个问题。

 

参考资料

Castle的官方网站http://www.castleproject.org

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2006-04-27 08:27 TerryLee 阅读(5163) 评论(19)  编辑 收藏 网摘 所属分类: [11]  数据持久[12]  开源世界

  回复  引用    
#1楼 2006-04-27 13:19 | LuLu [未注册用户]
准备学习AR,就靠这个系列了!
  回复  引用  查看    
#2楼 [楼主]2006-04-27 14:54 | Terrylee      
@LuLu

呵呵,Castle一句话:很棒!
  回复  引用  查看    
#3楼 2006-05-06 11:55 | 垃圾猪      
收藏,写得真好
  回复  引用  查看    
#4楼 [楼主]2006-05-06 12:27 | Terrylee      
@垃圾猪

谢谢支持:),其实更应该感谢Linkin大哥,没有他提出的问题,就不会有这篇文章了,呵呵~~~~
  回复  引用    
#5楼 2006-06-16 09:54 | wolfpzp [未注册用户]
看到这里对Castle有了一个大致的了解,感觉比NHibernate好用。
接下需要写点代码来实践一下。
在此谢谢Terrylee!
  回复  引用  查看    
#6楼 [楼主]2006-06-16 11:29 | TerryLee      
@wolfpzp

不用客气!

Castle ActiveRecord使用起来比NH简单一些,我也曾经说要写一个完整的例子,可是却一拖再拖,希望有时间能够兑现,呵呵:-)
  回复  引用  查看    
#7楼 2006-06-21 21:22 | 天生傻冒      
总算看完啦,对AR有了一点了解,希望看到后续的文章!
  回复  引用  查看    
#8楼 [楼主]2006-06-22 08:04 | TerryLee      
@天生傻冒
后续有时间,还会继续写的:-)
  回复  引用    
#9楼 2006-06-26 17:28 | ugvanx [未注册用户]
怎么直接调用Connection,然后执行Sql语句。有没有什么接口
  回复  引用  查看    
#10楼 [楼主]2006-06-26 17:39 | TerryLee      
@ugvanx

应该是可以的,我没注意这个问题
  回复  引用    
#11楼 2006-09-29 16:44 | codelover [未注册用户]
.net开源框架交流qq群30017484
  回复  引用    
#12楼 2006-10-11 15:50 | hope[匿名] [未注册用户]
欢迎大家加入我的Castle qq群 号码 275885
  回复  引用    
#13楼 2006-11-22 16:18 | kevinhgy [未注册用户]
收藏,写得真好
  回复  引用  查看    
#14楼 [楼主]2006-11-23 20:57 | TerryLee      
@kevinhgy
:)
  回复  引用    
#15楼 2006-12-05 21:55 | SkyDragon[匿名] [未注册用户]
这系列真爽呀,感谢@TerryLee的无私奉献,

从你这里学了不少东东,你那个设计模式系列什么时候更新咯,关注中.....
  回复  引用  查看    
#16楼 [楼主]2006-12-05 22:08 | TerryLee      
@SkyDragon[匿名]
:)

还得一段时间哦,最近都没时间写文章了,只是做了一些开源项目的推荐:)
  回复  引用  查看    
#17楼 2007-05-04 16:12 | leoxu      
哈哈。
文章写得很好。
有机会可以把这些系列文章做成CHM格式。
继续关注你下面的文章。
  回复  引用    
#18楼 2007-06-05 17:56 | mingxingw [未注册用户]
又没有整过,案例代码!大哥!
  回复  引用    
#19楼 2007-07-23 11:08 | 小蝎 [未注册用户]
你好,我想问一下,如果我想执行SQL语句或者存储过程该怎么执行呢?还有就是如果我有3或4个有关联的表,我该怎么进行查询呢?用多对多映射可以吗?如果可以该怎么写呢?如果不可以有什么好方法吗?如果用sql也是查多个表,那返回结果是object数组还是能直接绑定的呢?




标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-08-15 08:02 编辑过
Google站内搜索

相关文章:

相关链接: