天天看點

第八章、ADO.NET(SqlConnection、SqlCommand、SqlDataAdapter、SqlTransaction、SqlParameter、DataSet)

8.1、ADO.NET的命名空間和資料庫通路

ADO.NET是.NET平台提供的一種資料庫通路技術。下面按照命名空間的分類來說明ADO.NET的各類資料庫通路:

8.1.1、System.Data

定義和部分實作了ADO.NET體系結構的類、接口、委托和枚舉。

8.1.2、System.Data.SqlClient

       Sql Server的.NET Framework資料提供程式。這個是通路sql server資料庫的最佳方式,外界程式連接配接Sql server資料庫的連接配接字元串:

A、“Server = 172.25.112.136;database = vips;uid = vips_user;pwd = vips_user”

B、“Provider = SQLOLEDB.1;User ID = vips_user;password = vips_user;Initial Catalog = vips;Data Source = 172.25.112.136”

注意:

(1)、以上是可以不加Provider屬性的,因為SqlClient就是通路sql server的提供程式。

(2)、一般在一個伺服器上,隻會安裝一個版本的sql server資料庫,比如sql server2005、sql server2008等,安裝一個sql server資料庫的過程中,會産生一個資料庫執行個體,是以一般的資料庫伺服器上隻會有一個資料庫執行個體,正因為如此,上面的Server或者Data Source都隻賦予了IP位址,它們連接配接的就是那個資料庫執行個體。

但是如果一個伺服器上由于安裝了多個版本的sql server資料庫而産生了多個資料庫執行個體,或者同一個版本的資料庫安裝了多個資料庫執行個體,那麼在連接配接資料庫的時候,上面的Server或者Data Source就會被賦予IP\執行個體名。這個IP\執行個體名可以通過打開SQL Server Management Studio,登入的時候,按照下面的圖示來找到執行個體名:

第八章、ADO.NET(SqlConnection、SqlCommand、SqlDataAdapter、SqlTransaction、SqlParameter、DataSet)
第八章、ADO.NET(SqlConnection、SqlCommand、SqlDataAdapter、SqlTransaction、SqlParameter、DataSet)
第八章、ADO.NET(SqlConnection、SqlCommand、SqlDataAdapter、SqlTransaction、SqlParameter、DataSet)

(3)、對于sql server,一個執行個體下面可以對應很多使用者,但是隻會有一套表、視圖、函數、存儲過程等資料庫元素,這些元素是被所有使用者所共享的,隻是使用者操作這些元素的權限不一樣。

8.1.3、System.Data.OracleClient

    因為oracle不是微軟的東西,是以在using這個命名空間之前要添加引用,引用名字是:System.Data.OracleClient。

Oracle的.NET Framework資料提供程式,這個是通路oracle資料庫的最佳方式,外界程式連接配接Oracle 9i或者10g資料庫的連接配接字元串:  

A、“Server = net服務名;uid = scott;pwd = 123”

B、“User ID=pvpt;Password=pvpt;Data Source=PTJGZZ”

C、DataSource=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=172.25.112.50)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=orcl)));User ID=elias;password=elias;

     Max Pool Size=512;Connection lifetime=12

注意:

(1)、A、B兩種方式連接配接oracle的時候先要在機器上安裝一個oracle用戶端去配置一個網絡服務名;C方式則不需要。以上是可以不加Provider屬性的,因為OracleClient就是通路Oracle的提供程式。

(2)、對于oracle,在一個伺服器上由于安裝了多個版本的oracle資料庫而産生了多個資料庫執行個體,或者同一個版本的資料庫安裝了多個資料庫執行個體,(??????待确認)是以在這種情況下,連接配接字元串也要指定執行個體名(???????????待确認)

(3)、對于oracle,一個執行個體下面可以對應很多使用者,每個使用者都會有自己的一套表、視圖、函數、存儲過程等資料庫元素,其中一個使用者能夠通路另外一個使用者裡面的元素,但是這個通路有可能受到權限的控制。

8.1.4、System.Data.OleDb

OLEDB的.NET Framework資料提供程式。在這個命名空間中,提供了通路DB2、SQL

SERVER、ACESS、ORACLE、ODBC等資料的程式。他們對應的連接配接字元串中的provider

屬性是:DB2OLEDB、SQLOLEDB、Microsoft.jet.oledb.4.0、MSDAORA、MSDASQL,很顯然,OleDb可以通路以上提到的各種資料庫,但是對于sql server和oracle這兩種最為主流的資料庫來說,oledb并不是效率最高的,是以一般不用這個。

OleDb連接配接Acess資料庫的連接配接字元串(access一般在本地):

“provider = Microsoft.jet.oledb.4.0;data source = c:\mdbback\northwind.mdb”

注意:

這個隻是連接配接access資料庫的,它一般在本地,而且不需要使用者名和密碼,針對其他的資料庫,比如DB2、ODBC等可能不僅需要provider和data source屬性,還需要database、uid、pwd等屬性;此處的Provider屬性是必不可少的,因為oledb有很多的資料庫提供程式,要選擇一個具體的才可以;連接配接字元串是在每一種資料庫連接配接對象中都存在的,而不是某一種資料庫連接配接對象所特有的,每一種資料庫連接配接對象所對應的連接配接字元串都由若幹項組成,隻要滿足各種連接配接對象的需求即可,并不需要連接配接字元串中的屬性項數完全相同,但是屬性名對于各種連接配接對象都是通用的。

8.1.5、System.Data.Odbc

     其實在OleDb中,對ODBC資料庫的通路提供程式MSDASQL是無法使用的,因為在ADO.NET中有System.Data.Odbc專門提供了通路ODBC資料庫的程式。

8.2、ADO.NET的架構-----常用類

以sql server的資料庫通路提供者System.Data.SqlClient為例來讨論常用的類,對于其他資料庫通路的提供者來說,都有這些類,而且用法都是一樣的,隻是字首名不同而已。

8.2.1、SqlConnection

SqlConnection是一個連接配接對象,它的主要功能就是建立與資料庫伺服器的連接配接的,或者關閉這些連接配接,使用方法如下:

(1)、連接配接字元串的構成

A、Data Source(Server、Address、Addr、NetworkAddress)

這個屬性表示的是資料源,在sql server中就是伺服器的名稱+執行個體名或者IP位址+執行個體名,在oracle中是網絡服務名,在ACESS就是包括mdb資料庫檔案名在内的完整路徑。

B、Initial Catalog(Database)

這個屬性表示的是資料庫名字,是針對sql server的。

C、UID(User ID)

這個表示的是使用者ID。針對sql server和oracle的。

D、PWD(Password)

這個表示的是使用者密碼。針對sql server和oracle的。

E、Provider

    這個表示程式提供者的名稱,主要是針對Acess資料庫的通路的。

注意:以上的屬性都是不區分大小寫的。

(2)、建立一個連接配接對象

SqlConnection sqlConn = new SqlConnection();

(3)、給連接配接對象的連接配接字元串屬性指派

sqlConn.ConnectionString="Server=172.25.112.21;database=vips;uid = vips_user;pwd = vips_user";

(4)、打開連接配接

sqlConn.Open();

注意:一個已經打開了的連接配接在關閉之前是不能重複打開的。

(5)、關閉連接配接

sqlConn.Close();

注意:一個已經關閉了的連接配接在打開之前還可以再次關閉,不會報錯,是以可以在try ……catch……中兩次使用close,在try中已經關閉了連接配接,但是在關閉連接配接後面的代碼中出錯了,然後就跳到catch中來了,這個時候連接配接已經關閉了,那麼可以再次關閉。在catch中的close是很重要的,因為可能try中出異常的時候,連接配接還未關閉。

注意:

用戶端與伺服器的連接配接是非常昂貴的,它們占用了用戶端與伺服器各方的資源,讓一個不工作的連接配接長期處于連接配接狀态是不必要的,要及時的關閉這些不使用的連接配接。打開太多的與伺服器的連接配接會降低伺服器的速度,并且會阻礙建立新的連接配接。

連接配接池的概念:連接配接池是伺服器上的記憶體中的用來存放連接配接的一塊空間。資料庫與伺服器建立連接配接之後,這個連接配接就會在記憶體中的連接配接池中存放,即使連接配接關閉之後,這個連接配接池中的連接配接也不會馬上就釋放掉,而是被标記為未連接配接,當有新的連接配接請求發送過來的時候,伺服器就會去判斷連接配接池中是否有未使用的與請求相比對的連接配接,如果有就直接使用這個。建立一個全新的連接配接大概要幾秒鐘,但是使用連接配接池中的連接配接隻需要幾毫秒。

8.2.2、SqlCommand

(1)、定義一個SqlCommand對象

SqlCommand sqlComm = new SqlCommand();

(2)、給SqlCommand對象的連接配接屬性指派

sqlComm.Connection = sqlConn;

(3)、給SqlCommand對象指派要執行的指令   

     sqlComm.CommandText = "insert into t(id) values('1')";

(4)、SqlCommand對象的ExecuteNonQuery方法

這個函數是執行沒有傳回值指令的函數,隻針對對資料庫的非查詢動作,但是函數本身會傳回一個int值,表示指令執行後影響的資料庫的表的行數。

(5)、SqlCommand對象的ExecuteScalar方法

這個函數是傳回查詢的結果集中,第一行第一列的值,如果有多行多列會忽略掉其他的行和其他的列,傳回類型是object類型。這個函數是針對的select語句,一般是查詢結果隻有一行一列的最為适用。結果要經過轉換才能得到想要的類型。

注意:

(1)、以上的兩個函數的執行一定要在連接配接打開之後,否則會出錯的。

(2)、CommandText是接受sql語句的,可以使增删查改語句,也可以是執行存儲過程的指令,還可以是查詢,也就是說所有的DML操作以及查詢都是可以的,還可以是多條語句同時進行,語句之間用分号隔開即可。在一個連接配接打開後,可以通過改變CommandText的值來多次執行這兩個函數,然後才關閉連接配接,是以可以通過這種方式在一個連接配接未關閉的情況下按照順序執行多次sql語句。

(3)、對于ExecuteScalar,是針對那種查詢隻有一個值的的情況的,傳回object類型,如果查詢出來是多行多列,也隻會取第一行第一列。如果這種查詢查出來是行數為0,即什麼也沒查詢出來,那麼函數傳回的是C#中的null,此時用Tostring轉換就會出錯;如果查詢出來是一行一列,但是是資料庫中的null,那麼傳回的就是Convert.DBNull,那麼此時用Tostring轉換之後就是長度為0的字元串;如果查詢出來是一行一列,但是是資料庫中的長度為0的字元串,那麼傳回的就是object類型,此時用Tostring轉換之後就是長度為0的字元串。

8.2.3、SqlDataAdapter

資料擴充卡主要是針對查詢語句的,隻有當我們需要從資料庫中查詢出資料集的時候,才會用到擴充卡。

(1)、定義一個SqlDataAdapter對象

SqlDataAdapter sqlDA = new SqlDataAdapter();

(2)、給SqlDataAdapter對象的SelectCommand屬性指派

sqlDA.SelectCommand = sqlComm;

注意:這個前提是sqlComm的CommandText屬性是一個查詢語句。

(3)、填充到資料集

    sqlDA.Fill(ds);

注意:

(1)、Fill方法是把從資料庫中查詢到的資料集填充到DataSet中。在DataSet中是以表的形式存在。

(2)、可以往一個DataSet中填充多張表:Fill常用的有兩個函數重載,第一個是Fill(DataSet ds),這個隻有一個參數,那麼填充的時候會按照執行Fill填充的順序把多長表填充到DataSet中,通路的時候就按整數照索引來通路就可以了;第二個函數是Fill(DataSet ds,string srcTable),這個是在填充表格到資料集中的時候還要對這個表命一個名字,以便以後可以通過這個名字來通路這個表,表名是任意的,但是不可以重複,這種方式仍然可以按照填充的順序根據整數索引來通路,整數索引是從0開始的。

(3)、DataSet其實是C#中的一個概念,是獨立于ADO.NET的,當把資料庫中查詢到的資料集合填充到DataSet之後,與資料庫的連接配接就可以關閉了。

(4)、隻要調用了sqlDA.Fill(ds)函數,不管查詢的時候是否有結果,哪怕什麼也沒查詢出來(結果是0行),ds裡面也是有table的,可以用ds.Tables[0]來引用這張表,表結構與查詢的結構一緻;但是此時如果用ds.Tables[0].Rows[0]來引用第零行是錯誤的,因為根本就沒有行,此時要用ds.Tables[0].Rows.Count來判斷是否為0;但是如果有行,但是某行中的某個列是資料庫的null或者資料庫中的長度為0的字元串,那麼此時用Tostring轉換之後就是長度為0 的字元串。-----這個是DataSet的情況;

8.2.4、DataSet

資料集是一個容器,是用來存放表的,是表的集合,而表又是由行列共同構成的。

(1)、定義一個資料集對象

DataSet ds = new DataSet();

資料集有個表的集合:ds.Tables

表集合有個重要的屬性:表的數量:ds.Tables.Count

對表的引用可以根據Fill填充的時候命的表名,也可以用數字索引,一般都隻會填充一張表,而且也不會去命名,是以一般就直接:ds.Tables[0]來引用這張表。

注意:DataSet其實是C#中的一個概念,是獨立于ADO.NET的

8.2.4.1、資料表DataTable

定義一個資料表對象:DataTable dt = new DataTable();

-----------------------------------------------------------

資料表有行的集合:dt.Rows

行集合有個重要的屬性:行數:dt.Rows.Count

對行的引用隻能根據整數索引來引用:dt.Rows[0]

-----------------------------------------------------------

資料表也有列的集合:dt.Columns

列集合有個重要的屬性:列數:dt.Columns.Count

對列的引用可以根據索引:dt.Columns[0],也可以根據列名字元串:dt.Columns["vid"],表如果是從資料庫裡面查出來的,此處的列名就是資料庫表中的列名。

8.2.4.1.1、資料行DataRow

定義一個具體資料行對象:DataRow dr = new DataRow();

對該行中列值的引用:可以根據數值索引:dr[0],也可以根據列名字元串索引:dr["vid"]

8.2.4.1.2、資料列DataColumn

定義一個具體資料列對象:DataColumn dc = new DataColumn();

列有一個ColumnName屬性很重要,通過該屬性可以取得列名:dc.ColumnName;

對該列中行值的引用:隻能根據整數索引來引用:dc[0]---這種是錯誤的

8.2.4.2、在C#中建立Table并添加資料

            DataTable table = new DataTable("ComboBoxDataSource");

            table.Columns.Add("textFiled",typeof(string));

            table.Columns.Add("valueField",typeof(string));

            DataRow row;

            row = table.NewRow();

            row["textFiled"] = "a";

            row["valueField"] ="A";

            table.Rows.Add(row);

            row = table.NewRow();

            row["textFiled"] = "b";

            row["valueField"] ="B";

            table.Rows.Add(row);

            row = table.NewRow();

            row["textFiled"] = "c";

            row["valueField"] ="C";

            table.Rows.Add(row);

            row = table.NewRow();

            row["textFiled"] = "d";

            row["valueField"] ="D";

            table.Rows.Add(row);

注意:

(1)、以上所有的索引全部是從0開始的。

(2)、在C#語言中,數值類型的資料預設為0,引用類型的資料預設為null。

(3)、從資料庫裡面取出來的某行某列的一個值的類型是object類型的,要轉換成自己需要的類性。

(4)、資料庫裡面的NULL取出來後是object類型的,不等于C#中的null,用Tostring轉換之後就是長度為0 的字元串。資料庫裡面長度為0的字元串取出來後也不等C#中的null,用Tostring轉換之後就是長度為0 的字元串。C#中的NULL用Tostring轉換會出錯。

(5)、String.IsNullOrEmpty (string value)

這個是在C#中判斷字元串value是否為null或者長度為零的字元串,字元串中長度為0的字元串不是null,而是empty,沒有被建立的才是null。

(6)、Convert.DBNull:一個常數,表示資料庫中的null,這個是在C#中用來對比資料庫裡面取出來的null值的。 Convert.DBNull != C#中的null

Convert .IsDBNull (Object value)

傳回有關指定對象是否為 DBNull類型的訓示

8.2.5、SqlTransaction

ADO.NET中也有事務機制,規則如下:

          publicstatic void test1()

        {

            SqlConnection sqlConn =new SqlConnection();

            sqlConn.ConnectionString = SqlConnectionString;

            sqlConn.Open();

            SqlTransaction sqlTrans = sqlConn.BeginTransaction();

            SqlCommand sqlComm = new SqlCommand();

            sqlComm.Connection = sqlConn;

            sqlComm.Transaction = sqlTrans;

            try

            {

                sqlComm.CommandText = "insert into t values('111','111')";

                sqlComm.ExecuteNonQuery();

                sqlComm.CommandText = "insert into t values('122','122')";

                sqlComm.ExecuteNonQuery();

                sqlTrans.Commit();

                sqlConn.Close();

            }

            catch

            {

              //如果在執行的過程中,網絡斷開了,進來之後,直接執行sqlTrans.Rollback()就會出錯

              //因為此時該事務不再有效,那麼sqlTrans.Connection==null

                if (sqlTrans.Connection !=null)

                {

                    sqlTrans.Rollback();

                }

                sqlConn.Close();

            }

        }

注意:

(1)、事物是在同一個連接配接上面發生的。

(2)、在SQL SERVER 2005中,普通的sql語句執行一句,那麼就會直接往硬碟上寫一個結果而非事務;對于存儲過程沒使用事務的時候,也是執行一句就往硬碟上寫一個結果,但是如果使用了事務,那麼所有的執行全部在記憶體中,直到正常執行完成,commit送出才能寫到硬碟上。如果在正常送出之前出現異常,程式直接終止了,那麼記憶體中的資料就會丢失而不會寫到硬碟上,如果正常送出之前,出現異常,執行了rollback語句,那麼就復原了,不會寫到硬碟上面了。

(3)、ADO.NET中,普通的executenonquery函數執行之後,直接寫到硬碟上面,但是如果使用了事務的話,就不會了,會先寫到記憶體中,直到正常執行完成,commit送出才能寫到硬碟上。如果在正常送出之前出現異常,程式直接終止了,那麼記憶體中的資料就會丢失而不會寫到硬碟上,如果正常送出之前,出現異常,執行了rollback語句,那麼就復原了,不會寫到硬碟上面了。

(4)、可以使用try……catch……來捕獲異常,使用事務的時候一定要使用這個,異常是可以向外層傳遞的。

8.2.6、SqlParameter

       在8.2.2節中,已經說過,利用SqlCommand可以執行DML操作以及查詢,但是這些執行對sql指令是有限制的。對于一般的增删查改以及存儲過程的執行都沒有問題,隻要那些增删改查操作以及存儲過程的指令在C#裡面就能夠拼湊成sqlserver能夠識别的語句即可;但是對于一些特殊的情況,在C#裡面是沒有辦法拼湊出能夠讓sql server識别的指令的,比如:

(1)、新增一條資料,但是資料庫中有一個字段是image類型,這個類型将會存放一張圖檔,值在資料庫中是以位元組數組的形式存放的,在C#中,我們首先會将圖檔轉換成為位元組數組,但是位元組數組沒法與sql語句結合去拼湊成sql server識别的指令。

(2)、存儲過程中有些參數是output類型的,此時,也沒法通過一個C#變量與sql語句結合去拼湊成sql server識别的指令,來讓存儲過程的output類型的變量傳回值被C#變量擷取。

       是以,在這些特殊的情況下,就必須使用到SqlParameter,這個是對C#裡面的即将要傳給sql server的變量的封裝,下面以ADO.NET調用存儲過程的另外一種方式來講解SqlParameter的應用:

public staticvoid test()

         {           

            SqlConnection sqlConn =new SqlConnection();

            sqlConn.ConnectionString = SqlConnectionString;

            sqlConn.Open();

            SqlCommand sqlComm = new SqlCommand();

            sqlComm.Connection = sqlConn;

            sqlComm.CommandType =CommandType.StoredProcedure; //表示要執行的指令是存儲過程

            sqlComm.CommandText ="zwjtest";//表示要執行存儲過程的名字

            SqlParameter p1 = new SqlParameter("@p1",SqlDbType.Int);

            p1.Direction = ParameterDirection.Input;

            p1.Value = 5;

//存儲過程的形參有三個要素:名字、資料類型、輸出類型(input、Output

),在C#中,"@p1"對應資料庫形參的名字,注意前面要加@符号;SqlDbType.Int對應資料庫形參的資料類型;ParameterDirection.Input對應資料庫形參的輸出類型;p1.Value表示将要傳遞給資料庫形參的的值。

            SqlParameter p2 = new SqlParameter("@p2",SqlDbType.Int);

            p2.Direction = ParameterDirection.InputOutput;

            p2.Value = 10;

            SqlParameter p3 = new SqlParameter("returnValue",SqlDbType.Int);

            p3.Direction = ParameterDirection.ReturnValue;

            sqlComm.Parameters.Add(p1);

            sqlComm.Parameters.Add(p2);

sqlComm.Parameters.Add(p3);

            try

            {

                sqlComm.ExecuteNonQuery();

                int a = int.Parse(p2.Value.ToString());               

            }

            catch

            {             

                sqlConn.Close();

            }

         }

注意:

(1)、在sql server 2005中,Output形參不僅具有輸出也具有輸入的功能,形參不會去屏蔽實參原來的值,會被初始化為原來的值,相當與oracle中的inout;但是input就隻具有接收實參值的輸入功能;存儲過程是有預設的傳回值的(整型的),即使沒有return語句,它也會傳回一個值,但是當有return語句的時候,就以return傳回的整數值為主了。傳回值一定是int類型,如果自己去寫個字元串就錯了。

(2)、在C#中ParameterDirection主要有input、Output、InputOutput三種類型。

當隻輸入的時候,C#中ParameterDirection要用input,存儲過程的參數就要用input;當隻需要輸出的時候,C#中ParameterDirection也要用Output,存儲過程的參數就要

用Output,此時在C#中,不會對參數變量指派,在存儲過程中,我們也不會去寫代碼引用該參數的值,隻會給它指派;

當既需要輸入又需要輸出的時候,C#中ParameterDirection要用InputOutput,存儲過程的參數要用Output,此時在C#中會對參數變量指派,在存儲過程中,也會對實參指派,最後C#對應的變量會擷取到這個傳回值;

要想取得存儲過程的傳回值,還是要定義一個參數,添加到sqlComm.Parameters中,與存儲過程對應的形參是不存在的,是以參數名為"returnValue"資料類型是SqlDbType.Int,ParameterDirection是ReturnValue。

(3)、執行存儲過程可以用ExecuteNonQuery(),也可以用ExecuteScalar(),這兩個函數都可以,它們與存儲過程是否會return值,return什麼值沒有任何的關系,隻是去執行了存儲過程而已。執行ExecuteScalar的時候,不管有沒有return,這個的傳回值都是null。

(4)、所有C#的SqlParameter參數的value都是object類型的。

8.3、ADO.NET操作Oracle

ADO.N ET操作Oracle與ADO.N ET操作sql server是基本一緻的,在此隻說明一下不同點:

8.3.1、Oracle執行語句的機制(與sql server不同)

在Oracle中,普通的sql語句執行的時候,結果先寫到記憶體中,如果沒有送出,結果就在記憶體中,那麼之後本連接配接才可以看到結果,别的連接配接是無法看到結果的,除非送出了,才真正寫到硬碟上,别的連接配接才可以看到。送出的方式有很多,有commit,disconnect等。在存貯過程裡面也是這樣的,任何一個語句的執行都是在記憶體中,除非送出才會寫到硬碟上。

8.3.2、ADO.N ET執行Oracle語句

ADO.NET連接配接oracle的時候,普通的executenonquery函數執行之後,即使連接配接還沒有關閉,結果也直接寫到硬碟上面,本來理論上應該是在記憶體中的,但是因為ADO.NET是微軟做的,他就是為了統一起來,在執行的時候隐含了送出,是以就寫到硬碟中了。

8.3.3、ADO.N ET執行Oracle語句

如果使用了事務的話,就不會了,會先寫到記憶體中,直到正常執行完成,commit送出才能寫到硬碟上。如果在正常送出之前出現異常,程式直接終止了,那麼記憶體中的資料就會丢失而不會寫到硬碟上,如果正常送出之前,出現異常,執行了rollback語句,那麼就復原了,更不會寫到硬碟上面了。

8.3.4、ADO.N ET執行Oracle存儲過程

ADO.NET還可操作存儲過程,這個就是傳參數而已,執行全部在存儲過程裡面,那就按照oracle的存儲過程方式了而不會按照sqlserver方式了。

ADO.NET調用oracle存儲過程:

(1)、在定義OracleParameter(與oracle存儲過程的形參對應的實參)的時候,先要指定參數名:p1、p2、p3......等。

(2)、指定參數的資料類型:

在存儲過程參數傳遞的時候,會把實參的對參數的限制(長度、精度、小數位數等)一起傳遞給形參,之後形參的範圍就會按照實參的來限制。而形參本身是不能自己來定義限制條件的,否則就會出錯。但是對于表中定義字段或者存儲過程中定義變量,字元串的長度是一定要指定的,數值可以指定精度,也可以預設。

OracleType.Number與oracle存儲過程的Number對應,number不需要指定size。

OracleType.DateTime與oracle存儲過程的date對應,datetime不需要指定size。

OracleType.VarChar與oracle存儲過程的VarChar2對應:

對于in類型,形參隻是接受實參的值,在存儲過程中對形參隻能讀不可寫,這種情況下,實參不指定size或者指定為0,形參可以全部接收,但是如果指定了size,如果size小于實參的實際長度,那麼傳入的實參以size為主,否則以實際長度為主,一般不去指定長度;

對于out,實參指派也沒意義,但是形參是要指派,然後返還給實參的,這種情況,實參不指定size或者指定為0,那麼形參的最大的大小就是實參的實際長度,如果對實參的size指派,而且它的size就是實參最終的最大長度,實參不會接受形參的值,隻會将自己最終的值給形參,可以是null;一般都會指定。

對于in out,如果沒有指定size或者指定為0,那麼形參預設最大的大小是傳入的實參的位元組數目,實參會傳入全部的值,如果指定了size,如果size小于實參的實際長度,那麼傳入值以size為主,否則傳入值以實際長度為主,形參的最大長度以指定的size為主了。一般都會指定。

(3)、指定參數的ParameterDirection:

ParameterDirection.Input對應着oracle中的in:那麼實參隻是把值傳遞給形參,形參獲得實參傳遞的值之後,是隻讀的,不可寫,不能改變外面的值。

ParameterDirection. Output對應着oracle中的out:這種模式下面,不論實參是什麼值,都會被忽略不計,形參就像新定義的沒有被初始化的變量一樣,初值為null,這個變量具有讀寫屬性,存儲過程結束後,形參最終的值會賦給對應的實參。

ParameterDirection. InputOutput對應着oracle中的in out:這種模式下面,形參就像新定義的變量一樣,而且被初始化為實參的值了,這個變量具有讀寫屬性,存儲過程結束後,形參最終的值會賦給對應的實參。

(4)、指定參數的值。

(5)、指定參數的與存儲過程中對應的實參的名字。因為參數是與存儲過程中的形參名對應的,是以即使添加參數的時候,不按照形參的順序添加也沒關系。

(6)、對于存儲過程的異常,如果存儲過程中沒有異常捕獲而出了異常,那麼肯定會向外抛出異常的,這種事務是不會送出的;如果有捕獲了,但是在捕獲後,沒有抛出RAISE_APPLICATION_ERROR(-20101,flag || '³Ìzwj_prostudyÖ´ÐÐÒì³££º'|| SQLERRM);      那麼就認為存儲過程把異常處理了,外面就不會再次捕獲了,除非存儲捕獲異常之後抛出了才可以,捕獲異常之後如果復原就復原,送出就把之前成功執行的全部送出。

(7)、在oracle的存儲過程中,可以有return,但是不能添加傳回值,這個隻是用來結束存儲過程的。是以在C#裡面調用的時候,是無法通過OracleType. ReturnValue來獲得傳回值。

(8)、對于oracle的number類型的資料,在表定義、變量定義、存儲過程參數定義的時候都不需要指定精度,指定精度也可以(存儲過程參數除外);oracle的date類型任何時候都不要指定大小,因為它沒大小;oracle的varchar2類型的在表定義、變量定義的時候一定要加長度,在存儲過程的參數定義的時候一定不能加,存儲過程的參數長度是由實參決定的。

(9)、oracle定義變量的類型的時候,可以用一般的資料類型,還可以将類型指定為某個表的某個字段的類型:表名.列名%TYPE.這種對于普通的變量定義,那麼資料類型就跟指定的表名.列名完全一樣了,但是對于存儲過程的參數,則隻能通過這個指定類型,而不能指定長度,長度由外面的形參決定。

8.3.5、Oracle重号問題

在oracle中,如果有一張表的某個列是不能重複的,每次使用者取值的時候都是取出最大值加1,然後做一些别的計算,然後再次插入。如果是多個連接配接來取值,那麼可能出現多個連接配接取的最大值相同,然後都加1,最後導緻别的連接配接插入失敗。解決辦法:

(1)、讓這個列作為整數的自增長,插入的時候不用去管這個列,系統自動的+1,那麼就可以解決這個問題,這個是最理想的。

(2)、在oracle中,如果某個連接配接對某個表的某個列求出了最大值,然後就去做其他的很多的操作,最後做完了,及那個最大值加1然後構成新的行去插入到表中,那麼從求出最大值之後到插入到表中有一系列的操作,耗費了很多的時間,隻要在這個時間裡面有别的連接配接來操作,那麼就會取到相同的最大值,進而導緻最後出現最大值重複。針對這種情況,我們可以縮小取得最大值到最終更新資料庫的時間,那麼可以對這個流水号單獨建立一張表,這個表永遠隻記錄流水号的最大值,那麼取号的時候,立馬去更新這個号,最終某個連接配接取出最大值的時候,資料庫已經就是這個最大值了,那麼這個時間就很短,重複的幾率很小。這種方式有一個問題,因為在oracle中,取得号并且去更新這個号後,要送出,如果沒有送出,那麼别的連接配接取的号就是前面連接配接更新之前的号,雖然在前一個連接配接送出之前,現在的連接配接是無法去更新,但是它已經取得号了,這樣一定會重複的,是以更新之後要立即送出,那麼才能保證别的連接配接取得号是之前連接配接更新完了的号。

(3)、查詢的時候就鎖住表,然後取出最大值,加1,然後進行各種操作,完成之後再釋放。在鎖住後,讓外界連查詢都無法查詢。隻有做完了所有的操作釋放了表才可以讓别的連接配接來查詢。這種是在某一時刻隻能讓一個使用者來操作這個表,效率也不是很高。而且目前也不知道怎麼鎖住查詢。

(4)、可以用一個循環來操作失敗的連接配接,即讓失敗的連接配接的号再取一次,可以限制循環的次數。這種方式,當使用者很多的時候,有可能導緻最後的使用者循環多次才能成功,是以這個效率是很低級的。這個隻能是在使用者數比較少(循環次數比較少),多個使用者同時操作的幾率比較小,是可行的。為了更進一步的降低風險,防止死鎖,還可以讓目前線程停留幾秒鐘。

2009-02-28----2009-03-07

繼續閱讀