程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Castle ActiveRecord學習實踐(10) 深度分析Schema Pitfals

Castle ActiveRecord學習實踐(10) 深度分析Schema Pitfals

編輯:關於.NET

摘要:寫這篇文章緣於昨天跟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中執行了什麼操作:

public static void CreateSchema()
{
  CheckInitialized();
  foreach(Configuration config in ActiveRecordBase._holder.GetAllConfigurations())
  {
    SchemaExport export = CreateSchemaExport(config);
    try
    {
      export.Create( false, true );
    }
    catch(Exception ex)
    {
      throw new ActiveRecordException( "Could not create the schema", ex );
    }
  }
}

可以看到在ActiveRecord中,僅僅是調用了NHibernate中的SchemaExport的Create方法,並傳遞了兩個參數分別為fasle,true。現在我們跟蹤代碼到NHibernate中:

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

其中的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(255) null,
  blog_author NVARCHAR(255) null,
  primary key (blog_id)
)

同樣DropSchema也是這樣,不過justDrop為True罷了,它其實就執行了下面這句話:

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腳本進行分析,並通過IDbConnection,IDbCommand實現,看其中的一個方法:

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(255) null,
  blog_author NVARCHAR(255) null,
  primary key (blog_id)
)

所以,前面那句話,應該是在使用CreateSchemaFromFile()方法的情況有效。

四.GenerateCreationScripts和GenerateDropScripts

有時候,我們可以使用這兩個方法來生成創建或者刪除數據庫表的SQL腳本,然後再利用CreateSchemaFromFile()使用這些腳本。這兩個方法的使用很簡單:

ActiveRecordStarter.GenerateDropScripts("blog.sql");
ActiveRecordStarter.GenerateCreationScripts("blog.sql");

它也是調用了NHibernate中的相應的方法,將會在當前應用程序目錄下生成一個blog.sql的腳本文件。

這篇文章就分析到這兒,最後特別要感謝Linkin,沒有他提的問題,我也不會去深入的研究這其中的細節,在以後的文章中,我會更加認真的去對待每一個問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved