2009-05-14 5 views
30

Когда я поиск в Интернете для вставки BLOB-в базу данных Oracle с JDBC тонкого драйвера, большинство веб-страниц предлагаем 3-ступенчатый подход:усложненной Oracle JDBC обработка BLOB

  1. вставки empty_blob() значение.
  2. выберите строку с номером for update.
  3. Вставить реальное значение.

Это прекрасно работает для меня, вот пример:

Connection oracleConnection = ... 

byte[] testArray = ... 

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test (id, blobfield) values(?, empty_blob())"); 
ps.setInt(1, 100); 
ps.executeUpdate(); 
ps.close(); 
ps = oracleConnection.prepareStatement(
    "select blobfield from test where id = ? for update"); 
ps.setInt(1, 100); 
OracleResultSet rs = (OracleResultSet) ps.executeQuery(); 
if (rs.next()) { 
    BLOB blob = (BLOB) rs.getBLOB(1); 
    OutputStream outputStream = blob.setBinaryStream(0L); 
    InputStream inputStream = new ByteArrayInputStream(testArray); 
    byte[] buffer = new byte[blob.getBufferSize()]; 
    int byteread = 0; 
    while ((byteread = inputStream.read(buffer)) != -1) { 
     outputStream.write(buffer, 0, byteread); 
    } 
    outputStream.close(); 
    inputStream.close(); 
} 

Есть некоторые веб-страницы, в которых авторы предлагают использовать более простой 1 шаг решение. Предыдущий пример с этим решением:

Connection oracleConnection = ... 

byte[] testArray = ... 

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test(id, blobfield) values(?, ?)"); 
BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION); 
OutputStream outputStream = blob.setBinaryStream(0L); 
InputStream inputStream = new ByteArrayInputStream(testArray); 
byte[] buffer = new byte[blob.getBufferSize()]; 
int byteread = 0; 
while ((byteread = inputStream.read(buffer)) != -1) { 
    outputStream.write(buffer, 0, byteread); 
} 
outputStream.close(); 
inputStream.close(); 

ps.setInt(1, 100); 
ps.setBlob(2, blob); 
ps.executeUpdate(); 
ps.close(); 

Второй код гораздо проще, поэтому мой вопрос: Что такое точка первого (популярного) решения? Есть ли (было) какое-то ограничение для второго решения (номер версии сервера Oracle, версия драйвера jdbc, размер blob, ...)? Лучше ли первое решение (скорость, потребление памяти, ...)? Любые причины не использования более простого второго подхода?

Тот же вопрос применяется к полям CLOB.

ответ

5

Обработка LOB-сервера Oracle довольно бедна и может привести к серьезным проблемам с производительностью (например, массированное чрезмерное использование журнала повтора), поэтому первое решение может быть способом их устранения.

Я предлагаю попробовать оба подхода. если у вас есть компетентный администратор базы данных, они могут сообщить, какой из подходов имеет самое низкое влияние на сервер.

11

Подход обновления, упомянутый в первом случае, можно переписать с использованием чистого кода JDBC и таким образом уменьшить зависимость от классов, специфичных для Oracle. Это может быть полезно, если ваше приложение должно быть агностиком базы данных.

public static void updateBlobColumn(Connection con, String table, String blobColumn, byte[] inputBytes, String idColumn, Long id) throws SQLException { 
    PreparedStatement pStmt = null; 
    ResultSet rs = null; 
    try { 
    String sql = 
     " SELECT " + blobColumn + 
     " FROM " + table + 
     " WHERE " + idColumn + " = ? " + 
     " FOR UPDATE"; 
    pStmt = con.prepareStatement(sql, 
     ResultSet.TYPE_FORWARD_ONLY, 
     ResultSet.CONCUR_UPDATABLE); 
    pStmt.setLong(1, id); 
    rs = pStmt.executeQuery(); 
    if (rs.next()) { 
     Blob blob = rs.getBlob(blobColumn); 
     blob.truncate(0); 
     blob.setBytes(1, inputBytes); 
     rs.updateBlob(blobColumn, blob); 
     rs.updateRow(); 
    } 
    } 
    finally { 
    if(rs != null) rs.close(); 
    if(pStmt != null) pStmt.close(); 
    } 
} 

Для MSSQL Я понимаю, что синтаксис замок отличается:

String sql = 
    " SELECT " + blobColumn + 
    " FROM " + table + " WITH (rowlock, updlock) " + 
    " WHERE " + idColumn + " = ? " 
+1

Является ли агрегирование базы данных "FOR UPDATE"? Например, он не работает в SQL Server. Но, возможно, это вообще не важно для вышеупомянутой техники ... –

+0

Предложение FOR UPDATE инструктирует базу данных блокировать строки. Это важно в этом случае, потому что набор результатов будет обновлять эти строки. Если вы уверены, что никто не будет обновлять эти строки, вам может не понадобиться блокировка. –

+5

Вы не вставляете BLOB здесь, вы просто обновляете его. ОП был конкретно о вставке. –

5

Одна интересная вещь с JDBC, что вы можете обновить довольно агрессивно с последними драйверами и работать с функциями JDBC 4.0. Драйверы oracle JDBC будут работать со старыми версиями баз данных, поэтому вы можете использовать 11-граммовый JDBC-драйвер для базы данных 10g. База данных Oracle 11g JDBC поставляется в двух вариантах: ojdbc5.jar для Java 5 (т. Е. JDK 1.5) и ojdbc6.jar для Java 6 (т. Е. JDK 1.6). Ojdbc6.jar поддерживает новую спецификацию JDBC 4.0.

С новыми драйверами/JDBC 4.0 вы можете создать Blobs и символьные от объекта подключения:

Blob aBlob = con.createBlob(); 
int numWritten = aBlob.setBytes(1, val); 
+3

К сожалению, Oracle плохо выполнила стандартный JDBC-способ обработки BLOB (возможно, по бизнес-причинам). Вы вынуждены работать с конкретными классами оракулов, чтобы выполнить работу должным образом. – akarnokd

+0

Для меня это не работает: контейнер JEE и Oracle 10.2. –

+0

@ LuisSoeiro - Проверьте, какой драйвер JDBC используется. –

4

Это утверждение:

blob.setBytes(1, inputBytes); 

дает проблемы, когда я использую оракула тонкий клиент ojdbc14 ,баночка, «неподдерживаемые функции»

Таким образом, я должен был работать вокруг по:

rset.next(); 
Blob bobj = rset.getBlob(1); 
BLOB object = (BLOB) bobj; 
int chunkSize = object.getChunkSize(); 
byte[] binaryBuffer = new byte[chunkSize]; 
int position = 1; 
int bytesRead = 0; 
int bytesWritten = 0, totbytesRead = 0, totbytesWritten = 0; 
InputStream is = fileItem.getInputStream(); 
while ((bytesRead = is.read(binaryBuffer)) != -1) { 
bytesWritten = object.putBytes(position, binaryBuffer, bytesRead); 
position += bytesRead; 
totbytesRead += bytesRead; 
totbytesWritten += bytesWritten; 
is.close(); 
2

При условии, что данные CLOB достаточно мал, чтобы поместиться в памяти, не разрушив, вы можете просто создать подготовленное заявление и просто вызовите

ps.setString(1, yourString); 

Там могут быть и другие ограничения по размеру, но это, кажется, работает для размеров мы имеем дело с (500Кб) макс.

7

Другая точка зрения от Oracle DBA. Солнечные ребята очень плохо работали, когда разрабатывали стандарты JDBC (1.0, 2.0, 3.0, 4.0). BLOB означает большой объект, и поэтому он может быть очень большим. Это то, что нельзя хранить в куче JVM. Oracle думает о BLOB как о чем-то вроде дескрипторов файлов (это факт, что они называются «локаторы локализации»). LOBS нельзя создавать с помощью конструктора и не являются объектами Java. Кроме того, LOB-локаторы (oracle.sql.BLOB) не могут быть созданы с помощью конструктора - они ДОЛЖНЫ быть созданы на стороне БД. В Oracle существует два способа создания LOB.

  1. DBMS_LOB.CREATETEMPORATY - возвращенный локатор в этом случае указывает на временное табличное пространство. Все записи/чтения против этого локатора будут отправляться через сеть на сервер БД. В куче JVM ничего не хранится.

  2. Позвоните в функцию EMPTY_BLOB. ВСТАВЬТЕ В Т1 (ИМЯ, ФАЙЛ) ЗНАЧЕНИЯ («a.avi», EMPTY_BLOB()) ВЕРНУТЬ ФАЙЛ В? В этом случае вернутся точки локатора ловушек в табличное пространство данных. Все записи/чтения против этого локатора будут отправляться через сеть на сервер БД. Все записи «охраняются» путем записи в повторные журналы. В куче JVM ничего не хранится. Предложение возврата не поддерживалось стандартами JDBC (1.0, 2.0), поэтому вы можете найти много примеров в Интернете, где люди рекомендуют подход двух шагов: «INSERT ...; SELECT ... FOR UPDATE;»

Oracle большие объекты должны быть связаны с каким-то соединение с базой данных, они не могут быть использованы при DB соединение потеряно/закрыто/(или «совершил»). Они не могут быть переданы из одного соединения в другое.

Второй пример может работать, но потребует чрезмерного копирования, если данные из временного табличного пространства в табличное пространство данных.

+0

Итак, в вашем последнем случае, как будет написан код Java? Как следует «ВЕРНУТЬ ФАЙЛ?»? обрабатываться? –

+0

Я полностью не понимаю этот ответ в терминах того, что действительно позволяет JDBC 4.0, например, имея подготовленный оператор, такой как: stmt = conn.prepareStatement (значения «insert into storage (storage.storageid, storage.attachment)» (?, ?) "); ... stmt.setBlob (1, fInputStream); ... Что именно вы имеете в виду, когда говорите в Oracle, есть только два способа создания Blobs, поскольку выше оператор работает над Oracle (с драйвером ojdbc6.jar)? – ammianus

+2

OMG - Я просто хочу вставить несколько строк текста из строки в «BLOB» в схему, которую я унаследовал. Строка - это не МБ, не говоря уже о ГБ, данных, НО, иногда это превышает поразительный предел в 2000 символов. Какой цирк. – Roboprog

1

Я нашел простой звонок для setObject(pos, byte[]) работ для моего дела. Из базы данных Программирование с помощью JDBC и Java Джордж Риз,

 byte[] data = null; 
     stmt = con.prepareStatement("INSERT INTO BlobTest(fileName, " 
      + "blobData) VALUES(?, ?)"); 
     stmt.setString(1, "some-file.txt"); 
     stmt.setObject(2, data, Types.BLOB); 
     stmt.executeUpdate(); 
+1

Возможно, это хорошая идея, чтобы заставить тип, добавив [java.sql.Types.BLOB] (http://docs.oracle.com/javase/7/docs/api/java/sql/Types.html#BLOB) в список аргументов 'setObject'. – ceving

+0

Спасибо, @ceving. Я кое-что узнал. :) Я обновил ответ – Kirby

2

Некоторые watchouts найдено для второго решения

Я использую ojdbc6.jar - последней версии и заявления от «второго решение ':

BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION); 

Я должен освободить блоба после того, как заявление будет завершено - или в противном случае блоб закрываются, когда сессия закрыта (что может занять длительное время с пулом подключений) ,

blob.freeTemporary(); 

В противном случае вы можете увидеть заблокированные ресурсы:

select * from v$temporary_lobs 

Другой проблемой с временной BLOB-является необходимость выделения временных табличным: согласно документации http://docs.oracle.com/cd/E11882_01/appdev.112/e18294.pdf

Управления временных табличной для временного LOBs Временное табличное пространство используется для хранения временных данных LOB

0

Если размер вставки BLOB больше blob.getBufferSize(), сделка поручены как только первый фрагмент записывается в БД, как стоимости Autocommit имущества JDBC соединения по умолчанию является истинным и далее глыбы записи терпят неудачу, как db рассматривает их как новые транзакции. Предлагается следующее:
a) Установите для свойства autoCommit подключения jdbc значение false.

conn.setAutoCommit(false); 

б) явно зафиксировать транзакцию после загрузки всей BLOB.

while ((bytesRead = messageInputStream.read(buffer)) != -1) { 
    cumBytes += bytesRead; 
    blobOutputStream.write(buffer, 0, bytesRead); 
    } 
conn.commit();