Статьи

Давайте рассмотрим, как вставить Clob или Blob через JDBC

LOBs являются PITA во всех базах данных, а также в JDBC. Правильная обработка занимает несколько строк кода, и вы можете быть уверены, что в конце концов ошибетесь. Потому что вы должны подумать о нескольких вещах:

  • Прежде всего, большие объекты — это большие ресурсы, которые требуют особого управления жизненным циклом. После того, как вы выделили LOB, вам лучше «освободить» его, чтобы уменьшить нагрузку на ваш GC. Эта статья показывает больше о том, почему вам нужно освободить лоббов
  • Время, когда вы выделяете и освобождаете долю, имеет решающее значение. Он может иметь более длительный срок службы, чем любой из ваших ResultSet , PreparedStatement или Connection / транзакции. Каждая база данных управляет такими периодами жизни индивидуально, и вам может потребоваться прочитать спецификации в крайних случаях
  • Хотя вы можете использовать String вместо Clob или byte[] вместо Blob для небольших и средних больших объектов, это не всегда так, и может даже привести к некоторым неприятным ошибкам, таким как страшный Oracle ORA-01461: может связать LONG значение только для вставки в столбец LONG

Итак, если вы работаете на низком уровне с использованием JDBC (вместо абстрагирования JDBC через Hibernate или jOOQ ), вам лучше написать небольшую утилиту, которая позаботится о правильной обработке больших объектов .

Недавно мы заново открыли нашу собственную утилиту, которую мы используем для тестирования интеграции jOOQ, по крайней мере, в некоторых базах данных, и подумали, что это может быть очень полезно для пары наших читателей, которые работают непосредственно с JDBC. Рассмотрим следующий класс:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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

Чтобы использовать этот класс, просто напишите что-то вроде следующего:

1
2
3
4
5
6
7
8
9
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 и готово.

Если вы заинтересованы в том, почему вы должны вызывать Clob.free() или Blob.free() , прочитайте нашу статью об этом . Это OutOfMemoryErrors вас от одного или двух OutOfMemoryErrors