LOBs являются PITA во всех базах данных, а также в JDBC. Правильная обработка занимает несколько строк кода, и вы можете быть уверены, что в конце концов ошибетесь. Потому что вы должны подумать о нескольких вещах:
- Прежде всего, большие объекты — это большие ресурсы, которые требуют особого управления жизненным циклом. После того, как вы выделили LOB, вам лучше «освободить» его, чтобы уменьшить нагрузку на ваш GC. Эта статья показывает больше о том, почему вам нужно освободить лоббов
- Время, когда вы выделяете и освобождаете долю, имеет решающее значение. Это может иметь более длительный срок службы , чем любой из ваших
ResultSet
,PreparedStatement
илиConnection
/ сделки. Каждая база данных управляет такими периодами жизни индивидуально, и вам может потребоваться прочитать спецификации в крайних случаях - Хотя вы можете использовать
String
вместоClob
илиbyte[]
вместо больших и средних больших объектовBlob
, это не всегда так, и может даже привести к некоторым неприятным ошибкам, таким как страшный Oracle ORA-01461: может связывать значение LONG только для вставки в длинный столбец
Итак, если вы работаете на низком уровне с использованием JDBC (вместо абстрагирования JDBC через Hibernate или jOOQ ), вам лучше написать небольшую утилиту, которая позаботится о правильной обработке больших объектов .
Недавно мы заново открыли нашу собственную утилиту, которую мы используем для тестирования интеграции jOOQ, по крайней мере, в некоторых базах данных, и подумали, что это может быть очень полезно для пары наших читателей, которые работают непосредственно с JDBC. Рассмотрим следующий класс:
public class LOB implements AutoCloseable {
private final Connection connection;
private final List<Blob> blobs;
private final List<Clob> clobs;
public LOB(Connection connection) {
this.connection = connection;
this.blobs = new ArrayList<>();
this.clobs = new ArrayList<>();
}
public final Blob blob(byte[] bytes)
throws SQLException {
Blob blob;
// You may write more robust dialect
// detection here
if (connection.getMetaData()
.getDatabaseProductName()
.toLowerCase()
.contains("oracle")) {
blob = BLOB.createTemporary(connection,
false, BLOB.DURATION_SESSION);
}
else {
blob = connection.createBlob();
}
blob.setBytes(1, bytes);
blobs.add(blob);
return blob;
}
public final Clob clob(String string)
throws SQLException {
Clob clob;
if (connection.getMetaData()
.getDatabaseProductName()
.toLowerCase()
.contains("oracle")) {
clob = CLOB.createTemporary(connection,
false, CLOB.DURATION_SESSION);
}
else {
clob = connection.createClob();
}
clob.setString(1, string);
clobs.add(clob);
return clob;
}
@Override
public final void close() throws Exception {
blobs.forEach(JDBCUtils::safeFree);
clobs.forEach(JDBCUtils::safeFree);
}
}
Этот простой класс имеет несколько приятных лакомств:
- Это
AutoCloseable
значит, что вы можете освободить свои лобы с помощью оператора try-with-resources - Он абстрагируется от создания больших объектов на диалектах SQL. Не нужно помнить путь Oracle
Чтобы использовать этот класс, просто напишите что-то вроде следующего:
try (
LOB lob = new LOB(connection);
PreparedStatement stmt = connection.prepareStatement(
"insert into lobs (id, lob) values (?, ?)")
) {
stmt.setInt(1, 1);
stmt.setClob(2, lob.clob("abc"));
stmt.executeUpdate();
}
Это оно! Нет необходимости хранить ссылки на lob, безопасно освобождать его, если он не равен null, корректно восстанавливаться после исключений и т. Д. Просто поместите LOB
контейнер в оператор try-with-resources вместе с PreparedStatement
и done.
Если вас интересует, почему вы должны позвонить Clob.free()
или, Blob.free()
в первую очередь, прочитайте нашу статью об этом . Это избавит вас от одного или двухOutOfMemoryErrors