Oracle записать blob в файл

Обновлено: 05.07.2024

good work. Works well.

Small error in the SQL code lang="vb">SCRIPTION)", conn)) should be lang="vb">SCRIPTION)", connection))

Would be nice to see the include statements but there again VS2013 managed to find them for me.

I do not have problem with reading, writing and updating the blob in SQL. The problem I am having is that if I need to update information on the oracle database I cannot do it. I keep getting the following error message:

Update requires a valid UpdateCommand when passed DataRow collection with modified rows.Update failed

I am going to try your method of connection and reading the blob data.

Could you shed some light on using the Update Command with Oracle Blob Data please? Thanks.

I really enjoyed reading about your article. I quickly went to work following your instructions and it worked beautifully. The problem I am having now is that I cannot edit or revise the data when I pull up the Oracle data by using the Updateall() function. This same function works with SQL, but not with Oracle.

I trying to find a way to automaticly save emails from Outlook/Exchange (Inbound and Outbound) to an MS Sql server. It should then able to open the mails from the MS Sql server again.

Maybe I can use this to Serialize the mails and save them in SQL, and find a way to deserialize from SQL and open dem by Outlook.

I cannot find any product free or payable, that can do this.

Can anybody help.

Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
'''''''''''''''''''
' Read blob back from db
'''''''''''''''''''''''''''
'Dim iLenStream As Int32 =
'Dim barray(iLenStream) As Byte
Dim sConnSringLocalSql2005 As String = ""
sConnSringLocalSql2005 = "Data Source=(local);Initial Catalog=Test1;Persist Security Info=True;User /> '
Dim oConnLocal As New System.Data.SqlClient.SqlConnection
oConnLocal.ConnectionString = sConnSringLocalSql2005
On Error Resume Next
oConnLocal.Open()
If Err.Number <> 0 Then
Response.Write("
Err open conn local sql2005> " & Err.Description)
Response.Flush()
oConnLocal.Close()
'oFileStream.Close()
Exit Sub
End If
Dim oCmd As New System.Data.SqlClient.SqlCommand
oCmd.Connection = oConnLocal
oCmd.CommandType = Data.CommandType.StoredProcedure
oCmd.CommandTimeout = 6000
oCmd.CommandText = "spGetBlob2"
Dim Param1 As System.Data.SqlClient.SqlParameter
Param1 = oCmd.Parameters.Add("@BlobData", Data.SqlDbType.VarBinary, -1)
Param1.Direction = Data.ParameterDirection.Output
Param1.Value = Nothing
'
Dim Param2 As System.Data.SqlClient.SqlParameter
Param2 = oCmd.Parameters.Add("@Desc", Data.SqlDbType.VarChar, -1)
Param2.Direction = Data.ParameterDirection.Output
Param2.Value = Nothing
'
Dim Param3 As System.Data.SqlClient.SqlParameter
Param3 = oCmd.Parameters.Add("@idToFetch", Data.SqlDbType.Int, 4)
Param3.Direction = Data.ParameterDirection.Input
Param3.Value = 1
'
Dim Param4 As System.Data.SqlClient.SqlParameter
Param4 = oCmd.Parameters.Add("@iLenBlob", Data.SqlDbType.Int, 4)
Param4.Direction = Data.ParameterDirection.Output
Param4.Value = 0
'
On Error Resume Next
oCmd.ExecuteNonQuery()
If Err.Number <> 0 Then
Response.Write("
Err > " & oCmd.CommandText & " - " & Err.Description)
Response.Flush()
oConnLocal.Close()
Exit Sub
End If
'
Dim barray(Param4.Value) As Byte
barray = Param1.Value
Dim sFileName As String = "c:\windows\temp\test.pdf" 'Must be a folder where Network System writes
If Err.Number <> 0 Then
Response.Write("
FileStream Err > " & Err.Description)
Response.Flush()
End If
Dim oFS As New System.IO.FileStream(sFileName, IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.Write)

On Error Resume Next
ofs.Write(barray, 0, Param4.Value)
If Err.Number <> 0 Then
Response.Write("
FileStream Err > " & Err.Description)
Response.Flush()
End If
oConnLocal.Close()
oFS.Close()
''''''''''''''''''''''''''''''''''''''''''''''''
'create procedure spGetBlob2

'@BlobData varbinary(max) output,
'@Desc varchar(max) output,
'@idToFetch int,
'@iLenBlob int output

General News Suggestion Question Bug Answer Joke Praise Rant Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Для работы с данными большого объема СУБД Oracle предоставляет типы данных BLOB, CLOB, NCLOB и BFILE. Здесь LOB означает large object, или большой объект, и далее по тексту термины LOB и "большой объект" взаимозаменяемы. По сути, большой объект - это абстрактный тип для манипуляции данными большого объема внутри БД, а типы BLOB, CLOB, NCLOB и BFILE - его конкретные реализации.

Указанные типы данных можно использовать в СУБД Oracle для определения столбцов таблиц, атрибутов объектных типов и переменных PL/SQL.

Вот краткая характеристика этих типов:

  • BFILE (от binary file) - данные хранятся во внешнем по отношению к БД файле, а значение типа BFILE содержит указатель на файл; данные считаются двоичными.
  • BLOB (от binary large object) - данные хранятся в базе данных в отдельном сегменте * , а значение типа BLOB содержит указатель на них (LOB locator); данные считаются двоичными.
  • CLOB (от character large object) - данные хранятся в базе данных в отдельном сегменте * , а значение типа CLOB содержит указатель на них (LOB locator); данные интерпретируются как текст в кодировке базы данных (database character set).
  • NCLOB (от national character large object) - данные хранятся в базе данных в отдельном сегменте * , а значение типа CLOB содержит указатель на них (LOB locator); данные интерпретируются как текст в национальной кодировке (national character set)

* По умолчанию LOB'ы размером до 4000 байт хранятся непосредственно в строках таблицы (в табличном сегменте), а LOB'ы большего размера - в отдельном сегменте (возможно, в отдельном табличном пространстве). Это поведение регулируется опцией ENABLE|DISABLE STORAGE IN ROW команд CREATE TABLE и ALTER TABLE .

Итак, по месту хранения LOB'ы делятся на

  • внутренние (BLOB, CLOB, NCLOB), данные которых хранятся в БД, и
  • внешние (BFILE), данные которых хранятся в файлах операционной системы,

а по содержанию на

  • двоичные (BFILE и BLOB), для хранения данных в двоичных форматах, например, MP3, JPG, объектный код программ, и
  • текстовые (CLOB и NCLOB), для хранения данных в текстовых форматах, таких как XML, HTML, JSON, обычный текст.

Oracle 11g, согласно документации, работает с внутренними LOB'ами размером до 2 32 -1 байт и с BFILE файлами размером до 2 64 -1 байт.

Для работы с LOB'ами cоздам таблицу со столбцами соответствующих типов:

Вместе с таблицей были созданы сегменты для хранения больших объектов:

Для столбца типа BFILE отдельный сегмент не создан - ведь данные этого типа хранятся во внешних файлах.

Значение типа LOB может быть

  • NULL - неинициализировано, не содержит указателя на LOB,
  • пустым (empty) - указатель на LOB указывает в никуда,
  • непустым - указатель на LOB указывает на данные LOB'а.

Пустые LOB значения создаются функциями EMPTY_CLOB и EMPTY_BLOB :

Последний запрос демонстрирует два способа проверить, является ли LOB пустым. Запрос использует пакет DBMS_LOB , содержащий процедуры и функции для работы с LOB'ами.

Начиная с версии Oracle 9i в SQL и PL/SQL поддерживается неявная конвертация между (N)CLOB и VARCHAR2, что позволяет манипулировать значениями в (N)CLOB столбцах и переменных так, как будто это значения типа VARCHAR2:

Как видим, функции и операторы, работающие с VARCHAR2, перегружены для типа (N)CLOB! При этом преодолеваются ограничения в 4000 символов, свойственные SQL типу VARCHAR2:

А вот операторы сравнения для (N)CLOB работают только в PL/SQL и не работают в SQL:

Выше я воспользовался функциями TO_NCLOB и TO_CLOB для явной конвертации значений VARCHAR2 в значения (N)CLOB. В следующей таблице представлены все функции для конвертации в LOB типы и обратно:

ФункцияГде работает
TO_CLOB(character_data) SQL и PL/SQL
TO_BLOB(raw_data) SQL и PL/SQL
TO_LOB(long_data) SQL and PL/SQL
TO_NCLOB(character_data) SQL и PL/SQL
TO_RAW(blob_data) только PL/SQL

Как видим, функция TO_RAW недоступна в SQL и, отсюда, возможности конвертации между BLOB и RAW в SQL ограничены. Например:

Зато в PL/SQL работают явная и неявная конвертации между BLOB и RAW:

Рассмотренные возможности по работе со значениями LOB как с VARCHAR2 получили название SQL семантика для LOB'ов (SQL semаntics for LOBs). С их использованием связаны некоторые ограничения, как мы увидим ниже.

С точки зрения PL/SQL большие объекты делятся на:

  • временные (temporary), время жизни которых не превышает сеанса работы с СУБД,
  • постоянные (persistent), которые хранятся в базе данных или во внешнем файле.
  • создаются либо с помощью DBMS_LOB.CREATETEMPORARY , либо простым присваиванием значения LOB переменной в PL/SQL коде,
  • располагаются на диске во временном табличном пространстве (temporary tablespace),
  • могут быть проверены с помощью DBMS_LOB.ISTEMPORARY ,
  • освобождаются с помощью DBMS_LOB.FREETEMPORARY , что приводит к инвалидированию указателя на LOB,
  • в отличие от постоянных, изменяются без создания записей в журнале БД (logging) и не контролируются транзакциями,
  • могут быть скопированы в постоянные LOB'ы c помощью DBMS_LOB.COPY .

В вышеприведенных примерах с PL/SQL мы имели дело с временными LOB'ами.

Для работы с постоянными LOB'ами в PL/SQL нужно сначала получить указатель на LOB, а затем с его помощью извлекать или изменять данные, используя пакет DBMS_LOB . Следующий пример демонстрирует получение постоянного LOB'а и его потерю(!) при попытке изменить его значение простым присваиванием:

Дело в том, что SQL семантика для LOB'ов всегда порождает временные LOB'ы - это и есть то ограничение, о котором я упоминал выше. Неявное приведение VARCHAR2 к LOB (строка 7) или функция, перегруженная для (N)CLOB (строка 14), дают нам временные LOB'ы. Как только переменной PL/SQL, указывающей на постоянный LOB, присваивается временный LOB, переменная начинает указывать на временный LOB. А связь переменной с постоянным LOB'ом утрачивается.

Значение временного LOB'а можно сохранить в БД - и тем самым сделать постоянным - либо с помощью SQL либо, как уже упоминалось, с помощью DBMS_LOB.COPY . Продемонстрирую обе возможности:

Обратите внимание, что процедура DBMS_LOB.COPY заменила в постоянном NCLOB c3 только фрагмент, равный по размеру значению исходного NCLOB'а c2 . Как вариант, можно было перед копированием очистить LOB назначения с помощью DBMS_LOB.ERASE .

Изменения внутренних постоянных LOB'ов (в отличие от внешних или временных) в СУБД Oracle подчиняются транзакциям. Убедимся в этом, отменив только что сделанные изменения:

Типичный алгоритм для чтения или изменения постоянного LOB'а с помощью PL/SQL таков:

  1. Извлечь указатель на LOB из столбца таблицы с помощью SELECT .
  2. Открыть большой объект с помощью DBMS_LOB.OPEN .
  3. Получить оптимальный размер фрагмента для чтения (записи) LOB с помощью DBMS_LOB.GETCHUNKSIZE
  4. Получить размер LOB'а в байтах (для BLOB и BFILE) или символах (для CLOB и NCLOB) с помощью DBMS_LOB.GETLENGTH .
  5. Многократно вызывать DBMS_LOB.READ для чтения последовательных фрагментов LOB'а, пока не будут извлечены все данные
    ИЛИ
    многократно вызывать DBMS_LOB.WRITE , со смещением, или DBMS_LOB.WRITEAPPEND или иные процедуры DBMS_LOB для записи фрагментов данных.
  6. Закрыть LOB с помощью DBMS_LOB.CLOSE .

В предыдущем примере с DBMS_LOB.COPY я не открывал и не закрывал постоянный LOB при помощи DBMS_LOB.OPEN и DBMS_LOB.CLOSE , однако, это стоит делать для улучшения производительности при изменениях больших объектов.

Приведу пример выгрузки данных из постоянного CLOB'а во внешний файл. Для доступа к внешним файлам потребуется создать директорию, например:

Следующий код выгружает содержимое столбца lobs_tab.clob_col в файл clob_col1.txt , используя пакет DBMS_LOB для чтения CLOB и пакет UTL_FILE для записи во внешний файл:

Альтернативно, можно выгрузить CLOB во внешний файл, пользуясь SQL семантикой для LOB и не прибегая к DBMS_LOB :

Для обратной операции - загрузки содержимого файла в LOB - также можно воспользоваться пакетами UTL_FILE и DBMS_LOB , циклически читая данные из файла и записывая в LOB. Но интереснее сделать это с помощью типа данных BFILE.

Тип данных BFILE содержит указатель на внешний файл, который

  • состоит из двух частей: имя директории и имя файла,
  • создается с помощью функции BFILENAME , например, BFILENMAE('FILES_DIR', 'novel.txt') ,
  • может указывать на несуществующий файл.

Пакет DBMS_LOB позволяет читать содержимое BFILE, но не изменять его. Чтение из BFILE возвращает двоичные данные как тип данных RAW. Для преобразования в VARCHAR2, при необходимости, используется функция UTL_RAW.CAST_TO_VARCHAR2 .

Пример чтения BFILE и записи во временный BLOB:

В примере BFILE открывается и закрывается с помощью OPEN и CLOSE , аналогично внутренним LOB'ам. Также, пакет DBMS_LOB содержит несколько процедур и функций специально для работы с объектами BFILE:

Процедура / ФункцияЧто делает
FILEGETNAME возвращает имя директории и файла BFILE
FILEEXISTS проверяет, что файл BFILE существует
FILEOPEN открывает файл BFILE
FILEISOPEN проверяет, что файл BFILE открыт
FILECLOSE закрывает файл BFILE
FILECLOSEALL закрывает все открытые в сеансе файлы BFILE

Вместо чтения BFILE по частям пакет DBMS_LOB позволяет

  • с помощью LOADCLOBFROMFILE загрузить содержимое BFILE в CLOB, указав, какую кодировку (набор символов) имеет содержимое,
  • с помощью LOADBLOBFROMFILE загрузить содержимое BFILE в BLOB.

Пример загрузки текстового файла во временный CLOB (аналогично можно загрузить и в постоянный CLOB):

Значения src_offset и dest_offset отличаются, поскольку первое, для BFILE, выражено в байтах, а второе, для CLOB, выражено в символах. В файле и CLOB'е имеются девять двухбайтовых русских букв - напомню, их содержимое начинается с " привет, мир ".

Приведу неполный список процедур и функций DBMS_LOB для чтения, анализа и изменения значений BLOB, CLOB и NCLOB:

Процедура / ФункцияЧто делает
APPEND добавляет один LOB в конец другого
COPY копирует все или часть содержимого LOB'а в другой LOB
ERASE удаляет все или часть содержимого LOB'а
GETLENGTH возвращает размер LOB'а
INSTR ищет "подстроку" в LOB'е
ISOPEN проверяет, открыт ли LOB
ISTEMPORARY проверяет, временный ли LOB
READ читает данные LOB'а
SUBSTR получает "подстроку" из LOB'а
TRIM сокращает размер LOB'а до указанного
WRITE записывает данные в LOB
WRITEAPPEND записывает данные в конец LOB'а

Следующий эксперимент покажет разницу между внутренними и внешними постоянными LOB'ами. Помещу в поле bfile_col таблицы lobs_tab объект BFILE и скопирую единственную строку таблицы во вторую строку:

Команда INSERT привела к тому, что значения bfile_col в обеих строках связаны с одним и тем же внешним файлом, и его изменение отразится на обоих значениях.

А вот значения столбцов clob_col , nclob_col и blob_col для строк 1 и 2 стали независимы - не только указатели на LOB, но и данные внутренних LOB'ов в LOB-сегментах были скопированы. Продемонстрирую их независимость, изменив значения clob_col и nclob_col для строки 2:

Аналогично, при присваивании BLOB и (N)CLOB переменных в PL/SQL мы получаем независимые копии LOB'ов:

Итак, мы на примерах рассмотрели работу с большими объектами в SQL и PL/SQL. Работа с большими объектами имеет и другой аспект - это технология SecureFiles, позволяющая, в частности, сжимать хранимые в LOB-сегментах данные, свести к минимуму их дублирование, шифровать эти данные. Но эта тема выходит за рамки данного очерка.

my student_t1 table consists of a blob column called image. I wrote a base64 function that correctly converts the BLOB into a CLOB. Now, I need to write this CLOB to my IO_DIR on my server /u01/app/oracle/io_dir .

When the following PL/SQL code is executed, the CLOB gets written to the directory as a text file. I believe the entire CLOB is not outputted though because when I try to decode the image is corrupted (only the upper portion of the image comes in). I ran it on a small image (11k) and it worked fine. I also monitored that l_pos is increasing correctly so it seems like it is reading each chunk of the CLOB (32kB) What am I doing wrong?

I am using Oracle 11g Express Edition (XE) and SQL Developer. Here is the error and the code


42.1k 9 9 gold badges 22 22 silver badges 48 48 bronze badges 51 1 1 gold badge 1 1 silver badge 5 5 bronze badges

3 Answers 3

Try using a smaller buffer size than 32767, in particular, one with size no more than 8191.

As far as I can tell, DBMS_LOB.SUBSTR does not always behave itself if the buffer size exceeds 8191 characters. If you ask for it to read (say), 10000 characters, it might only read 8191 from the LOB, but it will return to you a 10000-character long string where the first 8191 characters were what it read from the LOB and the other 1809 were what happened to be there before (e.g. from the last call to DBMS_LOB.SUBSTR ). If the buffer size is 8191 characters or smaller, this problem doesn't occur and DBMS_LOB.SUBSTR returns the number of characters you asked for.

I cannot fathom why this function would do this. It seems very odd. I can only conclude that it is a bug in the database. Whether it is specific to Oracle 11g, or XE, I can't say.

The Oracle documentation for DBMS_LOB.SUBSTR includes the following under Usage Notes, which doesn't explain the problem but does include a mention of the number 8191:

  • DBMS_LOB.SUBSTR will return 8191 or more characters based on the characters stored in the LOBs. If all characters are not returned as a consequence of the character byte size exceeding the available buffer, the user should either call DBMS_LOB.SUBSTR with a new offset to read the remaining characters, or call the subprogram on loop until all the data is extracted.

Finally, my copy of Oracle 11g XE is using a single-byte character set. The number 8191 may change if you are using a multi-byte character set.

Вопросам использования больших объектов LOB (Large Object Bynary) зачастую приходится уделять пристальное внимание. Связано это с хранением различных файлов в таблицах базы данных (БД). Сразу же возникает вопрос, зачем хранить в БД файл, если потом придется извлекать его назад опять же в виде в файла? Ведь можно создать отдельное файловое хранилище. Ответ также оказывается простым и тривиальным - для файлового хранилища требуется дополнительно решать вопросы репликации и синхронизации, предоставление прав доступа и создание backup'a. Таким образом, если нельзя обойтись без LOB-объектов, то остается выбрать их тип - бинарный (BLOB Binary Large Object ) или символьный, называемый в БД различных провайдеров по-разному (CLOB, TEXT).

ПРИМЕЧАНИЕ : при работе с символьными данными необходимо учитывать кодировку. Это вопрос будет учтен при записи текстового файла в таблицы БД и выгрузки из БД.

Описание примера

В статье рассматривается пример записи и чтения нескольких типов файлов в поля BLOB и CLOB (TEXT). В качестве серверов БД были использованы Oracle 10g Enterprise Edition, Oracle 10g Express Edition и MySQL 5.1.

Пример представляет собой проект Eclipse, реализующий все принципы ООП (объектно-ориентированное программирование) - наследование, инкапсуляция и полиморфизм. Структура проекта представлена на следующем скриншоте.


В директории «lib» размещаются библиотеки JDBC для подключения к серверам БД. При подключении к серверу 10g Enterprise Edition использовалась библиотека ojdbc7.jar. В случае использования сервера 10g Express Edition были использованы библиотеки ojdbc14_g.jar, ojdbc14.jar. Для подключения в IDE Eclipse библиотеки к проекту необходимо ее выделить и в контекстном меню выбрать "Build Path/Add to Build Path". Чтобы отключить использование библиотеки в проекте используйте вкладку "Java Build Path" в окне свойств проекта.

Пример включает следующие программные модули :

DAOBase.java базовый модуль создания подключения к серверу БД и работы с полями BLOB и CLOB (TEXT);
MainTest.java главный модуль тестирования методов чтения и записи LOB;
MySQLDAO.java модуль создания подключения к серверу MySQL, наследует DAOBase.java;
OracleDAO.java модуль создания подключения к серверу Oracle, наследует DAOBase.java;
Файлы для тестирования aircraft.jpg, отчет.xlsx, текст.txt

Приложение в двух разнотипных БД создает таблицы с полями BLOB и CLOB (TEXT), в которые сначала файлы записываются, потом выполняется их чтение. Представленные процедуры записи и чтения больших объектов Вы можете использовать в своих приложениях практически без доработок.

Создание подключений Connection к различным серверам БД описано здесь и в данной статье не рассматривает (код представлен). Пример можно скачать здесь.

Описание базового модуля, DAOBase.java

Базовый модуль включает следующие основные методы :

МетодНазначениеПримечание
public void createConnection() Создание Connection, подключение к БД Переопределяется для каждого провайдера БД
public Connection getConnection() Получение Connection
public void closeConnection() Закрытие Connection
public boolean execSQL (sql) Выполнение SQL-запроса
public boolean writeBlob(table, field, pk, id, fpath) Запись файла в поле BLOB таблицы
public long readBlobToFile(table, field, pk, id, fpath) Извлечение объекта BLOB в файл
public boolean writeClob(table, field, id, fpath) Запись файла в поле CLOB таблицы
public long readClobToFile(table, field, id, path) Извлечение объекта CLOB в файл
public String readClobData(table, field, id) Чтение объекта CLOB

Параметры процедур записи и чтения больших объектов LOB включают наименование таблиц table, наименование LOB-полей field, наименование поля первичного ключа pk, идентификатор записи id и путь к файлу fpath. При необходимости Вы можете доработать данные процедуры и включить в качестве параметра наименование схемы Schema(Oracle) или базы данных Database (MySQL). Не исключено, что и первичный ключ может содержать несколько полей.

ПРИМЕЧАНИЕ :
1. Следует обратить внимание, что методы работы с полями BLOB и CLOB не переопределяются согласно представленной выше таблице. Но это касается баз данных Oracle и MySQL, используемые в примере. Т.е. можно сказать, что методы JDBC «справляются» с полями LOB данных провайдеров. К тому же в MySQL используется тип TEXT, а в Oracle CLOB.
2. Можно использовать различные методы записи в поля LOB содержимое файлов, что будет продемонстрировано на примере MySQL.

Ниже представлен листинг DAOBase.java, где методы работы с полями BLOB и CLOB не включают код. Это сделано преднамеренно для наглядного представления общей структуры модуля DAOBase.java. В противном случае листинг сильно увеличится и «за деревьями нельзя будет увидеть лес». Код методов работы с полями LOB описан далее.

Листинг DAOBase.java

Процедуры создания createTable (sql) и удаления dropTable(sql) таблицы БД не включены в общий список методов DAOBase.java, поскольку в примере выполняют вспомогательную функцию и используется для «подыгрыша». Как правило, структура БД меняется редко и нет необходимости включать в приложение процедуры, которые не используются.

Листинг методов чтения и записи данных в поле BLOB

Две вспомогательные функции readBlobField и writeFromBlob2Stream имеют модификаторы private и используются основным методом readBlobToFile при выгрузке файла из БД.

Для записи файла в поле BLOB используется метод setBinaryStream, получающий в качестве параметра поток FileInputStream. При выгрузке файла из БД в методе readBlobToFile сначала читается объект BLOB (readBlobField), после этого он записывается в выходной поток OutputStream в методе writeFromBlob2Stream, где используется метод чтения в бинарный поток getBinaryStream() объекта BLOB.

Листинг процедур чтения и записи данных в поле CLOB

Две вспомогательные функции readClobField и readFromClob2Stream имеют модификаторы private и используются основными методами readClobToFile, readClobData при выгрузке файла из БД.

Чтобы учесть кодировку символов при записи текстового файла в БД используется InputStreamReader и BufferedReader. Для записи файла в поле CLOB/TEXT используется метод setCharacterStream объекта CLOB, которому передается BufferedReader. При выгрузке файла из БД в методе readClobToFile сначала читается объект CLOB (readClobField ), после этого он записывается в выходной поток BufferedWriter в методе readFromClob2Stream, где используется метод чтения в символьный поток getCharacterStream() объекта CLOB.

Листинг OracleDAO.java

Листинг модуля OracleDAO.java включает переопределенные метод createConnection() и метод создания процедур БД createTable. Структуры таблиц, описанные в константах TABLE_blobs и TABLE_files в виде SQL-скриптов, включают только поля идентификаторов id и поля data соответствующих типов LOB. По умолчанию поля дата имеют значения NULL, поскольку используются в методах при обновлении.

При создании объекта в конструкторе сразу же создается подключение к серверу БД. В качестве свойства объекту подключения передается кодировка устанавливаемого соединения "utf8".

Листинг MySQLDAO.java

Листинг модуля MySQLDAO.java включает переопределенные метод createConnection(), метод создания процедур БД createTable и метод writeClob, который демонстрирует другой подход записи символьного файла в поле TEXT. В данном методе файловый поток FileInputStream сразу же перенаправляется в поток setAsciiStream. Но чтобы не исказить символы использовалась соответствующая кодировка в таблице БД и подключении к серверу. Родительский метод writeClob класса DAOBase.java также сработал без нареканий. Таким образом, в Вашем распоряжении 2 подхода записи текстового файла в БД MySQL.

Структуры таблиц, описанные в константах TABLE_blobs и TABLE_files в виде SQL-скриптов, включают только поля идентификаторов id и поля data соответствующих типов LOB. Для таблицы "files" установлена кодировка "UTF8". По умолчанию поля дата имеют значения NULL, т.к. используются в методах при обновлении.

При создании объекта в конструкторе сразу же создается подключение к серверу БД, которому в качестве свойства передается кодировка устанавливаемого соединения "utf8".

Тестирование примера

Главный модуль приложения MainTest.java выполняет тестирование методов записи файлов в БД и выгрузки в файл. Основной метод тестирования testDAO в качестве параметра принимает базовый класс (полиморфизм). В методе testDAO сначала проверяется установка подключения к серверу БД. Если подключение установлено, то создаются таблицы, в которые записываются и извлекаются файлы.

Результаты тестирования

Исходный код рассмотренного примера записи и чтения больших объектов LOB с использованием JDBC можно скачать здесь (7.27 Мб).

При тестировании примера необходимо определить параметры подключения к Вашему серверу БД - схема (база данных), логин и пароль.


PL/SQL: BLOB, CLOB, NCLOB и BFILE

Oracle и PL/SQL поддерживают несколько разновидностей типов данных, предназначенных специально для работы с большими объектами ( LOB, Large OBjects ). Такие объекты позволяют хранить огромные (от 8 до 128 терабайт) объемы двоичных (например, графических) или текстовых данных.

До выхода Oracle9i Release2 в объектах LOB можно было хранить до 4 Гбайт данных. Начиная с Oracle10g, ограничение было повышено до величины от 8 до 128 терабайт (конкретное значение зависит от размера блока вашей базы данных).

В PL/SQL можно объявлять большие объекты четырех типов:

  • BFILE — двоичный файл. Переменная этого типа содержит локатор файла, указывающий на файл операционной системы вне базы данных. Oracle интерпретирует содержимое файла как двоичные данные.
  • BLOB — большой двоичный объект. Переменная этого типа содержит локатор LOB , указывающий на большой двоичный объект, хранящийся в базе данных.
  • CLOB — большой символьный объект. Переменная этого типа содержит локатор LOB , указывающий на хранящийся в базе данных большой блок текстовых данных в наборе символов базы данных.
  • NCLOB — большой символьный объект с поддержкой символов национальных языков ( NLS ). Переменная этого типа содержит локатор LOB , указывающий на хранящийся в базе данных большой блок текстовых данных с национальным набором символов.

Большие объекты можно разделить на две категории: внутренние и внешние. Внутренние большие объекты (типы BLOB, CLOB и NCLOB ) хранятся в базе данных и могут участвовать в транзакциях на сервере базы данных. Внешние большие объекты (тип BFILE ) представляют двоичные данные, хранящиеся в файлах операционной системы вне таблиц базы данных. Они не могут участвовать в транзакциях, то есть вносимые в них изменения нельзя сохранить или отменить в зависимости от результата транзакции. Целостность данных обеспечивается только на уровне файловой системы. Кроме того, повторное чтение из BFILE может приводить к разным результатам — в отличие от внутренних больших объектов, соответствующих модели логической целостности чтения.

LONG И LONG RAW

Вероятно, спецы, знакомые с Oracle, заметили, что мы до сих пор не упоминали о типах данных LONG и LONG RAW . И это не случайно. Конечно, в столбцах типа LONG и LONG RAW базы данных можно хранить большие объемы (до 2 Гбайт) соответственно символьных и двоичных данных. Однако максимальная длина соответствующих им переменных PL/SQL значительно меньше: всего лишь 32 760 байт, что даже меньше длины переменных VARCHAR2 и RAW (32 767 байт). С учетом столь странного ограничения в программах PL/SQL лучше использовать переменные типа VARCHAR2 и RAW , а не типа LONG и LONG RAW.

Значения типов LONG и LONG RAW , извлекаемые из базы данных и содержащие более 32 767 байт данных, не могут присваиваться переменным типа VARCHAR2 и RAW . Это крайне неудобное ограничение, из-за которого типы LONG и LONG RAW лучше вообще не применять.

Эти типы официально считаются устаревшими и поддерживаются только для сохранения обратной совместимости кода. Компания Oracle не рекомендует ими пользоваться, и я с ней полностью согласен. В новых приложениях вместо них лучше использовать типы CLOB и BLOB . А для существующих приложений в документации Oracle SecureFiles and Large Objects Developer’s Guide приводятся рекомендации по преобразованию данных типа LONG в данные типа LOB .

Читайте также: