在 .Net 中使用大對象
作者:Jason Price
了解如何使用 .Net 讀寫大對象 (LOB) 以及如何通過 BFILE 讀取數據。
本文相關下載:
· 示例代碼
· Oracle 數據庫 10g
· ODP.Net(版本 10.1.0.2.0 或更高版本,如果需要的話)
· Microsoft .Net Framework 和 SDK
本文是上一篇介紹在 .NET 中使用數據庫對象文章的後續文章。在本文中,您將了解如何在 Visual Basic .NET (VB.NET) 和 Visual C# .NET (C#) 中使用大對象。尤其是,您將了解如何使用 .NET 讀寫大對象 (LOB)。您還將了解如何通過 BFILE(可以將其看作是文件指針)讀取數據。本文中引用的所有腳本和文件都可在這裡找到。本文假定您大體上了解 C# 和 VB.Net 編程。
本文適用於開發人員,您應大體上了解 C# 和 VB.Net 編程並具有 LOB 基礎知識。如果您需要 LOB 的介紹,可以閱讀 Oracle LOB 文檔,也可以閱讀我編寫的 Oracle 數據庫 10g SQL(McGraw-Hill/Osborne,2004)一書。
所需軟件
如果您要跟隨我們逐步完成本文中給出的示例,那麼您需要安裝以下軟件:
class="bodycopy">訪問已安裝好的 Oracle 數據庫
注意:如果您使用的是 Oracle 數據庫 10g(Oracle8i 版本 3 8.1.7 或更高版本)之前的版本,則需要在數據庫之外單獨下載和安裝 Oracle Data Provider for .NET (ODP.Net)。
ODP.NET 驅動程序針對 Oracle 數據庫訪問進行了優化,因此可以獲得最佳性能,並且它們還支持 Oracle 數據庫的豐富特性,如 BFILE、BLOB、CLOB、XMLType 等。如果您正在開發基於 Oracle 數據庫的 .NET 應用程序,那麼就特性和性能來講,ODP.Net 無疑是最佳的選擇。
注意:ODP.NET 驅動程序針對 Oracle 數據庫訪問進行了優化,因此可以獲得最佳性能,並且它們還支持 Oracle 數據庫的豐富特性,如 BFILE、BLOB、CLOB、XMLType 等。如果您正在開發基於 Oracle 數據庫的 .NET 應用程序,那麼就特性和性能來講,ODP.Net 無疑是最佳的選擇。
數據庫模式設置
首先,您需要設置將包含本文所用表的數據庫模式。您首先必須創建一個名為 lob_user 的用戶,並按如下所示授予該用戶所需權限。(您必須先以具有數據庫管理員權限的用戶身份登錄數據庫,才能創建用戶和授予權限):
您會在示例代碼文件 lob_db.sql 中找到前兩個語句和該部分中出現的設置 store 模式的其他語句。
以下語句創建一個名為 SAMPLE_FILES_DIR 的目錄,該目錄指向服務器硬盤驅動器上的 C:\sample_files 目錄;您必須在硬盤驅動器的 C: 分區中創建 sample_files 目錄,並將 textContent.txt 和 binaryContent.doc 文件復制到 C:\sample_files 中。
注意:textContent.txt 和 binaryContent.doc 文件包含莎士比亞戲劇 Macbeth 中的引文。您不久將看到把這兩個文件的內容復制到數據庫中。
下一語句授予公眾讀取權限,以便所有用戶均可讀取 SAMPLE_FILES_DIR 的內容:
下一語句以 lob_user 的身份連接:
您將看到本文使用了三個表:
以下語句創建 clob_content、blob_content 和 bfile_content 等表:
如果您創建了這些表所用的模式不是 lob_user 的模式,那麼您將需要修改稍後的示例程序中的模式名稱。
下兩條語句向 clob_content 和 blob_content 表中添加一個空的 CLOB 和 BLOB:
下列 PL/SQL 語句將文件 textContent.txt 中的文本加載到 clob_content 表中,將文件 binaryContent.doc 中的二進制數據加載到 blob_content 表中:
下一條語句使 bfile_content 表中的 BFILE 指向位於 SAMPLE_FILES_DIR 目錄中的 textContent.txt 文件:
使用 C# 和 VB.Net 從 LOB 中讀取數據
檢索 LOB 有兩種方法:
在第二種情況下,第一個數據庫往返從 LOB 讀取的數據量取決於 OracleDataReader 對象的 InitialLOBFetchSize 屬性的設置,它的值從 OracleCommand 對象中繼承。InitialLOBFetchSize 的默認值為 0 — 即 LOB 數據檢索將推遲到程序顯式請求該數據(即第一種情況)時進行。如果將 InitialLOBFetchSize 更改為大於零的值,則在一個往返中將立即檢索 LOB 數據,最多為在 InitialLOBFetchSize 中指定的字節或字符數。
此參數影響 SELECT 語句執行中的所有 LOB。例如,如果將 InitialLOBFetchSize 設置為 5K,且在 SELECT 語句執行中將檢索 10 個 LOB,則在一個數據庫往返中將檢索這 10 個 LOB 中每個 LOB 的前 5K。當前,10.1.0.2.0 版的 ODP.Net 中 InitialLOBFetchSize 的最大設置為 32KB。Oracle 將在未來版本中將此最大大小增大為 2GB。
注意:如果更改 InitialLOBFetchSize(默認值 0),則您只能使用上面提到的方法 2 中的訪問器從 CLOB 中讀取數據 — 不過 Oracle 計劃移除該限制。在未來的 ODP.Net 版本中,您將能夠使用這兩個方法來檢索 LOB。
如果您選擇的所有 LOB 的數據量不大,且選擇了很多 LOB,則通過更改 InitialLOBFetchSize(默認值 0),您可能獲得更出色的立即檢索 LOB 數據性能。如果使用 InitialLOBFetchSize,則應將其設置為一個略大於所選 LOB 大小的 80% 的值。例如,如果行中 LOB 大小的 80% 小於或等於 1KB,則應將 InitialLOBFetchSize 設置為 1KB。由於結果將取決於網絡性能、延遲、數據大小等因素,因此應試驗您的設置以發現 InitialLOBFetchSize 的最優設置。
更改 InitialLOBFetchSize 的默認值 0 時,將進行立即 LOB 檢索。當保留 InitialLOBFetchSize 的默認值零 0 時,將進行延遲 LOB 檢索。下表提供了決定使用延遲 LOB 檢索還是立即 LOB 檢索時要考慮的原則。
在以下情況下使用延遲 LOB 檢索 在以下情況下使用立即 LOB 檢索 客戶端和數據庫服務器之間的網絡帶寬不足 網絡帶寬充足 您不需要立即對大部分 LOB 數據進行立即訪問,而是可以隨著時間的推移來檢索它 您需要在選中 LOB 數據時立即讀取幾乎所有這些數據 您正在執行更新、插入或刪除,不打算讀取 LOB N/A
使用延遲 LOB 檢索從 LOB 中讀取數據
接下來,我們將逐步完成四個示例程序(使用 LOB 定位器讀取先前存儲在 clob_content.clob_column 中的文本和存儲在 blob_content.blob_column 中的二進制數據)中的主要步驟。這四個程序如下所示:
注意:有關如何編譯 C# 和 VB.NET 程序的信息,請閱讀我的技術文章“在 .Net 中使用 Oracle 數據庫事務”。
第 1 步
ClobExample1.cs 中的第 1 步從 clob_content 表中讀取行:
在 ClobExample1.vb 中,VB.Net 代碼為
BlobExample1.cs 中的第 1 步從 blob_content 表中讀取行:
在 BlobExample1.vb 中,VB.Net 代碼為
第 2 步
ClobExample1.cs 中的第 2 步將 LOB 定位器復制給 OracleClob 對象。使用 myOracleDataReader.GetOracleClob() 方法取得定位器:
在 ClobExample1.vb 中,VB.Net 代碼為
BlobExample1.cs 中的第 2 步將 LOB 定位器復制給 OracleBlob 對象。使用 myOracleDataReader.GetOracleBlob() 方法取得定位器:
在 BlobExample1.vb 中,VB.Net 代碼為
第 3 步
ClobExample1.cs 中的第 3 步使用 OracleClob 對象的 Read() 方法取得 CLOB 數據。Read() 方法有兩個版本: 其中:
int Read(byte [] byteArray, int offset, int count)
int Read(char [] charArray, int offset, int count)
在 ClobExample1.cs 的以下代碼中您將看到,從 CLOB 讀取字符並將其寫入名為 charArray 的字符數組中。此示例代碼將在每個數據庫往返中一次讀取 CLOB 50 個字符,直到讀取了整個 CLOB。
在 ClobExample1.vb (VB.Net) 中:
BlobExample1.cs 中的第 3 步使用 OracleBlob 對象的 Read() 方法。Read() 方法只有一個版本:
在 BlobExample1.cs 的以下 C# 代碼中您將看到,從 BLOB 讀取字符並將其寫入名為 byteArray 的字符數組中。此示例代碼將在每個數據庫往返中一次讀取 BLOB 50 個字符,直到讀取了整個 BLOB。
在 BlobExample1.vb 中,VB.Net 代碼為
使用立即 LOB 檢索從 LOB 中讀取數據
現在,我將逐步完成四個示例程序(使用立即 LOB 檢索讀取先前存儲在 clob_content.clob_column 中的文本和存儲在 blob_content.blob_column 中的二進制數據)中的主要步驟。這四個程序如下所示:
我還將在第 1 步中向您演示如何更改 InitialLOBFetchSize。正如我在前面提到的,將 InitialLOBFetchSize 更改為一個大於零的值將導致立即檢索數目最大為在 InitialLOBFetchSize 中指定的字節或字符數的 LOB 數據。
第 1 步
第 1 步將 InitialLOBFetchSize 設置為 1,000 個字節。在 ClobExample2.cs 和 BlobExample2.cs 中,C# 代碼為
注意,是在 OracleCommand 對象上設置 InitialLOBFetchSize。在 ClobExample2.vb 和 BlobExample2.vb 中,VB.Net 代碼為
第 2 步
第 2 步與前面的延遲 LOB 檢索示例顯示的第 1 步相同。
第 3 步
ClobExample2.cs 中的第 3 步使用 GetString() 方法從 CLOB 中取得數據並顯示該數據:
在 ClobExample2.vb 中,VB.Net 代碼為
BlobExample2.cs 中的第 3 步使用 GetBytes() 方法從 BLOB 中取得數據並顯示讀取的字節數:
在 BlobExample2.vb 中,VB.Net 代碼為
使用 C# 和 VB.Net 寫入 LOB
使用 OracleDataReader 對象時有兩種方法可以寫入 LOB:
至於選擇哪種方法完全取決於個人偏好。方法之間不存在性能優劣。對於任何一種方法,當您在 OracleClob 或 OracleBlob 中使用 CLOB 或 BLOB 定位器後,應使用 OracleClob 或 OracleBlob 的 Write() 方法寫入 LOB。使用定位器訪問 LOB 的優點是,在寫入 LOB 之前,最初不必將數據下載到中間層。
使用方法 1 寫入 CLOB
我將逐步完成兩個示例程序(使用方法 1 寫入 clob_content.clob_column 中的 CLOB)中的主要步驟。這兩個程序如下所示:
由於寫入 BLOB 的步驟相似,因此我只演示寫入 CLOB 的代碼。
第 1 步
必須在 OracleTransaction 對象的上下文中執行所有 LOB 更新。在 ClobExample3.cs 中,C# 代碼為
在 ClobExample3.vb 中,VB.Net 代碼為
第 2 步
第 2 步是讀取行,它與前面的“使用延遲 LOB 檢索從 LOB 中讀取數據”部分中的第 1 步相同。
第 3 步
第 3 步是使用 OracleDataReader 的 GetOracleClobForUpdate() 方法取得 LOB 定位器。在 ClobExample3.cs 中,C# 代碼為
在 ClobExample3.vb 中,VB.Net 代碼為
第 4 步
第 4 步是使用 OracleClob 對象的 Write() 方法寫入 CLOB。Write() 方法有兩個版本:
其中:
在 ClobExample3.cs 的以下 C# 代碼中,注意我首先使用 Erase() 方法刪除了 myOracleClob 的當前內容;寫入 myOracleClob 之前不必執行該操作,執行該操作只是為了刪除現有文本。
在 ClobExample3.vb 中,VB.Net 代碼為
第 5 步
第 5 步是提交事務,以便將新文本永久存儲在數據庫中。在 ClobExample3.cs 中,C# 代碼為
在 ClobExample3.vb 中,VB.Net 代碼為
使用方法 2 寫入 CLOB
現在,我們將逐步完成兩個示例程序(使用方法 2 寫入 clob_content.clob_column 中的 CLOB)中的主要步驟。這兩個程序如下所示:
只有第 2 步和第 3 步與方法 1 中所示步驟不同。
第 2 步
第 2 步從 clob_content 中讀取行。對於方法 2,將 FOR UPDATE 子句添加到 SELECT 語句。在 ClobExample4.cs 中,C# 代碼為
在 ClobExample4.vb 中,VB.Net 代碼為
第 3 步
第 3 步取得 CLOB 定位器。在 ClobExample4.cs 中,C# 代碼為
在 ClobExample4.vb 中,VB.Net 代碼為
以下是該步驟與前一方法中所示步驟的差別:此處我調用 GetOracleClob(),而前面我調用了 GetOracleClobForUpdate()。由於第 2 步中使用了 FOR UPDATE 子句,因此我不必使用 GetOracleClobForUpdate() — 我只需調用 GetOracleClob() 並開始寫入 CLOB。一旦擁有 CLOB 定位器,寫入 CLOB 的步驟則與上面的方法 1 完全相同。
使用 C# 和 VB.Net 從 BFILE 中讀取數據
BFILE 存儲文件系統中文件的指針,您可以通過該指針訪問該文件。我將逐步完成兩個示例程序(讀取由 bfile_content.bfile_column 中的 BFILE 指向的 textContent.txt 文件中的文本)中的主要步驟。這兩個程序如下所示:
第 1 步
第 1 步從 bfile_content 表中讀取行。在 BfileExample1.cs,C# 代碼為
在 BfileExample1.vb 中,VB.Net 代碼為
第 2 步
第 2 步通過調用 OracleDataReader 對象的 GetOracleBFile() 方法取得 BFILE。在 BfileExample1.cs 的以下 C# 代碼中您將看到,我還將顯示目錄名、文件名以及文件是否存在:
在 BfileExample1.vb 中,VB.Net 代碼為
第 3 步
BfileExample1.cs 中的第 3 步打開 BFILE:
在 BfileExample1.vb 中,VB.Net 與上一程序相同。
第 4 步
BfileExample1.cs 中的第 4 步使用 Read() 方法從 BFILE 中讀取數據。在以下代碼中您將看到,BFILE 中的數據被讀入名為 byteArrray 的字節數組中;同時,還使用 System.Text.Encoding.ASCII.GetString() 方法將該數組中的數據轉換為字符串,以便在屏幕上顯示數據:
在 BfileExample1.vb 中,VB.Net 代碼為
第 5 步
BfileExample1.cs 中的第 5 步使用 Close() 方法關閉 BFILE:
在 BfileExample1.vb 中,VB.Net 與上一程序相同。
結論
本文介紹了如何從 C# 和 VB.Net 訪問大對象。您還了解了如何通過 BFILE 讀取數據。
myOracleBFile.Close();
Dim byteArray(1000) As byte
Dim offset As Integer = 0
Dim numBytesRead As Integer
numBytesRead = myOracleBFile.Read(byteArray, offset, 1000-offset)
Do While (numBytesRead > 0)
Console.WriteLine("numBytesRead = " & numBytesRead)
Dim text As string = _
System.Text.Encoding.ASCII.GetString(byteArray, 0, numBytesRead)
Console.WriteLine("text = " & text)
offset += numBytesRead
numBytesRead = myOracleBFile.Read(byteArray, offset, 1000-offset)
Loop
byte [] byteArray = new byte[1000];
int offset = 0;
int numBytesRead;
while ((numBytesRead = myOracleBFile.Read(byteArray, offset,
1000-offset)) > 0)
{
Console.WriteLine("numBytesRead = " + numBytesRead);
String text =
System.Text.Encoding.ASCII.GetString(byteArray, 0, numBytesRead);
Console.WriteLine("text = " + text);
offset += numBytesRead;
}
myOracleBFile.OpenFile();
Dim myOracleBFile As OracleBFile = _
myOracleDataReader.GetOracleBFile(1)
Console.WriteLine("myOracleBFile.DirectoryName = " & _
myOracleBFile.DirectoryName)
Console.WriteLine("myOracleBFile.FileName = " & _
myOracleBFile.FileName)
Console.WriteLine("myOracleBFile.FileExists = " & _
myOracleBFile.FileExists)OracleBFile myOracleBFile = myOracleDataReader.GetOracleBFile(1);
Console.WriteLine("myOracleBFile.DirectoryName = " +
myOracleBFile.DirectoryName);
Console.WriteLine("myOracleBFile.FileName = " +
myOracleBFile.FileName);
Console.WriteLine("myOracleBFile.FileExists = " +
myOracleBFile.FileExists);myOracleCommand.CommandText = _
"SELECT id, bfile_column " & _
"FROM bfile_content " & _
"WHERE id = 1"
Dim myOracleDataReader As OracleDataReader = _
myOracleCommand.ExecuteReader()
myOracleDataReader.Read()myOracleCommand.CommandText =
"SELECT id, bfile_column " +
"FROM bfile_content " +
"WHERE id = 1";
OracleDataReader myOracleDataReader =
myOracleCommand.ExecuteReader();
myOracleDataReader.Read();Dim myOracleClob As OracleClob = _
myOracleDataReader.GetOracleClob(1)OracleClob myOracleClob = myOracleDataReader.GetOracleClob(1);myOracleCommand.CommandText = _
"SELECT id, clob_column " & _
"FROM clob_content " & _
"WHERE id = 1 FOR UPDATE"
Dim myOracleDataReader As _
OracleDataReader = myOracleCommand.ExecuteReader()
myOracleDataReader.Read()myOracleCommand.CommandText =
"SELECT id, clob_column " +
"FROM clob_content "; +
"WHERE id = 1 FOR UPDATE";
OracleDataReader myOracleDataReader =
myOracleCommand.ExecuteReader();
myOracleDataReader.Read();myOracleTransaction.Commit()myOracleTransaction.Commit();myOracleClob.Erase()
Dim text As string = "It is the east, and JulIEt is the Sun"
Dim charArray() As char = text.ToCharArray()
myOracleClob.Write(charArray, 0, charArray.Length)
Console.WriteLine("myOracleClob.Value = " & myOracleClob.Value)myOracleClob.Erase();
string text = "It is the east, and JulIEt is the Sun";
char [] charArray = text.ToCharArray();
myOracleClob.Write(charArray, 0, charArray.Length);
Console.WriteLine("myOracleClob.Value = " + myOracleClob.Value);Write(byte [] byteArray, int offset, int count)
Write(char [] charArray, int offset, int count)Dim myOracleClob As _
OracleClob = myOracleDataReader.GetOracleClobForUpdate(1)OracleClob myOracleClob = myOracleDataReader.GetOracleClobForUpdate(1);Dim myOracleTransaction As OracleTransaction = _
myOracleConnection.BeginTransaction()OracleTransaction myOracleTransaction = myOracleConnection.BeginTransaction();Dim byteArray(1000) As byte
Dim numBytesRead As long = _
myOracleDataReader.GetBytes(1, 0, byteArray, 0, 1000)
Console.WriteLine("numBytesRead = " & numBytesRead)byte [] byteArray = new byte[1000];
long numBytesRead = myOracleDataReader.GetBytes(1, (long) 0, byteArray, 0, 1000);
Console.WriteLine("numBytesRead = " + numBytesRead);Dim clobData As string = myOracleDataReader.GetString(1)
Console.WriteLine("clobData = " & clobData)String clobData = myOracleDataReader.GetString(1);
Console.WriteLine("clobData = " + clobData);myOracleCommand.InitialLOBFetchSize = 1000myOracleCommand.InitialLOBFetchSize = 1000;Dim byteArray(50) As byte
Dim numBytesRead As Integer
numBytes
Read = _
myOracleBlob.Read(byteArray, 0, 50)
Do While (numBytesRead > 0)
Console.WriteLine("numBytesRead = " & numBytesRead)
numBytesRead = _
myOracleBlob.Read(byteArray, 0, 50)
Loopbyte [] byteArray = new byte[50];
Console.WriteLine("byteArray.Length = " + byteArray.Length);
int numBytesRead;
while ((numBytesRead = myOracleBlob.Read(byteArray, 0, 50)) > 0)
{
Console.WriteLine("numBytesRead = " + numBytesRead);
}int Read(byte [] byteArray, int offset, int count)Dim charArray(50) As char
Dim numCharsRead As Integer
numCharsRead = myOracleClob.Read(charArray, 0, 50)
Do While (numCharsRead > 0)
Console.WriteLine("numCharsRead = " & numCharsRead)
Dim clobData As New string(charArray, 0, numCharsRead)
Console.WriteLine("clobData = " & clobData)
numCharsRead = myOracleClob.Read(charArray, 0, 50)
Loopchar [] charArray = new char[50];
int numCharsRead;
while ((numCharsRead = myOracleClob.Read(charArray, 0, 50)) > 0)
{
Console.WriteLine("numCharsRead = " + numCharsRead);
string clobData = new string(charArray, 0, numCharsRead);
Console.WriteLine("clobData = " + clobData);
}Dim myOracleBlob As _
OracleBlob = myOracleDataReader.GetOracleBlob(1)OracleBlob myOracleBlob = myOracleDataReader.GetOracleBlob(1);Dim myOracleClob As _
OracleClob = myOracleDataReader.GetOracleClob(1)OracleClob myOracleClob = myOracleDataReader.GetOracleClob(1);myOracleCommand.CommandText = _
"SELECT id, blob_column " & _
"FROM blob_content " & _
"WHERE id = 1"
Dim myOracleDataReader As _
OracleDataReader = myOracleCommand.ExecuteReader()
myOracleDataReader.Read()myOracleCommand.CommandText =
"SELECT id, blob_column " +
"FROM blob_content " +
"WHERE id = 1";
OracleDataReader myOracleDataReader =
myOracleCommand.ExecuteReader();
myOracleDataReader.Read();
myOracleCommand.CommandText = _
"SELECT id, clob_column " & _
"FROM clob_content " & _
"WHERE id = 1"
Dim myOracleDataReader As _
OracleDataReader = myOracleCommand.ExecuteReader()
myOracleDataReader.Read()myOracleCommand.CommandText =
"SELECT id, clob_column " +
"FROM clob_content " +
"WHERE id = 1";
OracleDataReader myOracleDataReader =
myOracleCommand.ExecuteReader();
myOracleDataReader.Read();INSERT INTO bfile_content (
id,
bfile_column
) VALUES (
1,
BFILENAME(''SAMPLE_FILES_DIR'', ''textContent.txt'')
);DECLARE
my_clob CLOB;
my_blob BLOB;
my_bfile BFILE;
BEGIN
-- load the CLOB
my_bfile := BFILENAME(''SAMPLE_FILES_DIR'', ''textContent.txt'');
SELECT clob_column
INTO my_clob
FROM clob_content
WHERE id = 1 FOR UPDATE;
DBMS_LOB.FILEOPEN(my_bfile, dbms_lob.file_readonly);
DBMS_LOB.LOADFROMFILE(my_clob, my_bfile, DBMS_LOB.GETLENGTH(my_bfile), 1, 1);
-- load the BLOB
my_bfile := BFILENAME(''SAMPLE_FILES_DIR'', ''binaryContent.doc'');
SELECT blob_column
INTO my_blob
FROM blob_content
WHERE id = 1 FOR UPDATE;
DBMS_LOB.FILEOPEN(my_bfile, dbms_lob.file_readonly);
DBMS_LOB.LOADFROMFILE(my_blob, my_bfile, DBMS_LOB.GETLENGTH(my_bfile), 1, 1);
DBMS_LOB.FILECLOSEALL();
COMMIT;
END;
/INSERT INTO clob_content (
id, clob_column
) VALUES (
1, EMPTY_CLOB()
);
INSERT INTO blob_content (
id, blob_column
) VALUES (
1, EMPTY_BLOB()
);CREATE TABLE clob_content (
id INTEGER PRIMARY KEY,
clob_column CLOB NOT NULL
);
CREATE TABLE blob_content (
id INTEGER PRIMARY KEY,
blob_column BLOB NOT NULL
);
CREATE TABLE bfile_content (
id INTEGER PRIMARY KEY,
bfile_column BFILE NOT NULL
);CONNECT lob_user/lob_passWord;GRANT READ ON DIRECTORY SAMPLE_FILES_DIR TO PUBLIC;CREATE OR REPLACE DIRECTORY SAMPLE_FILES_DIR AS ''C:\sample_files'';CREATE USER lob_user IDENTIFIED BY lob_passWord;
GRANT CONNECT, RESOURCE, CREATE ANY DIRECTORY TO lob_user;