콘텐츠로 이동

LOB

LOB#

이 절은 Altibase가 제공하는 LOB 타입의 데이터를 JDBC 애플리케이션에서 조작하는 방법을 설명한다.

전제 조건#

  • Altibase가 지원하는 LOB 데이터 타입은 BLOB 및 CLOB이 있으며, 각각 4GB-1byte의 최대 크기를 가질 수 있다. 단, JDK 1.6 이상에서만 가능하다.

  • LOB 데이터를 정상적으로 다루기 위해서는 세션의 autocommit 모드가 아래의 조건 중 하나를 만족해야 한다.

    • Connection 객체의 setAutoCommit(false)을 사용해서 세션의 자동커밋을 해제(disable)하고 사용자가 수동으로 트랜잭션을 제어해야 한다.

    • Clientside_auto_commit을 on으로 지정하여 JDBC 드라이버가 트랜잭션의 자동커밋을 제어하도록 한다.

BLOB 사용하기#

아래는 JDBC 애플리케이션에서 BLOB 데이터를 조작하는 방법을 코드 예제로 보여준다.

PreparedStatement 객체를 통해서 BLOB 데이터 쓰기#

다음은 예제에서 사용되는 테이블을 생성하는 구문이다.

CREATE TABLE TEST_TABLE ( C1 BLOB );
1. InputStream 객체와 setBinaryStream 메소드를 사용한 방법#
InputStream sInputStream = ...
long sLength = ...
... 
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE VALUES (?)");
...
sPstmt.setBinaryStream(1, sInputStream, sLength);
...
sPstmt.execute();
...

JDK 1.5에서는 sPstmt를 AltibasePreparedStatement 타입으로 캐스팅 하면 long 타입의 길이 변수로 정의된 setBinaryStream() 메소드를 호출할 수 있다.

import Altibase.jdbc.driver.AltibasePreparedStatement;
...
((AltibasePreparedStatement)sPstmt).setBinaryStream(1, sInputStream, sLength);
...
2. OutputStream 객체와 setBinaryStream 메소드를 사용한 방법#
byte[] sBuf = ...
... 
PreparedStatement sPstmt = connection().prepareStatement("SELECT * FROM TEST_TABLE FOR UPDATE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    Blob sBlob = sPstmt.getBlob(1);
    OutputStream sOutputStream = sBlob.setBinaryStream(1);
    sOutputStream.write(sBuf);
    sOutputStream.close();
    ...
}
...
sPstmt.execute();
...
3. Blob 객체와 setBlob 메소드를 사용한 방법#
java.sql.Blob sBlob = ...
...
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE
VALUES (?)");
...
sPstmt.setBlob(1, sBlob);
...
sPstmt.execute();
...
4. Blob 객체와 setObject 메소드를 사용한 방법#
java.sql.Blob sBlob = ...
...
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE
VALUES (?)");
...
sPstmt.setObject(1, sBlob);
...
sPstmt.execute();
...
5. setObject 메소드에 SQL 타입을 지정한 방법#
java.sql.Blob sBlob = ...
...
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE
VALUES (?)");
...
sPstmt.setObject(1, sBlob, AltibaseTypes.BLOB);
...
sPstmt.execute();
...

ResultSet 객체를 통해서 BLOB 데이터 쓰기#

다음은 예제에서 사용되는 테이블을 생성하는 구문이다.

CREATE TABLE BLOB_TABLE ( BLOB_COLUMN BLOB );
1. InputStream 객체와 updateBinaryStream 메소드를 사용한 방법#
InputStream sInputStream = ...
long sLength = ...
...
PreparedStatement sPstmt = connection().prepareStatement("SELECT BLOB_COLUMN FROM BLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateBinaryStream(1, sInputStream, sLength);
    sRs.updateRow();
    ...
}
...
2. Blob 객체와 updateBlob 메소드를 사용한 방법#
java.sql.Blob sBlob = ...

...

PreparedStatement sPstmt = connection().prepareStatement("SELECT BLOB_COLUMN FROM BLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateBlob(1, sBlob), ;
    sRs.updateRow();
    ...
}

...
3. Blob 객체와 updateObject 메소드를 사용한 방법#
java.sql.Blob sBlob = ...
...  
PreparedStatement sPstmt = connection().prepareStatement("SELECT BLOB_COLUMN FROM BLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateObject(1, sBlob);
    sRs.updateRow();
    ...
}
...
4. updateObject 메소드에 SQL 타입을 지정한 방법#
java.sql.Blob sBlob = ...

...

PreparedStatement sPstmt = connection().prepareStatement("SELECT BLOB_COLUMN FROM BLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateObject(1, sBlob, AltibaseTypes.BLOB);
    sRs.updateRow();
    ...
}
...

SELECT … FOR UPDATE 구문으로 BLOB 데이터 갱신#

byte[] sBytes = new byte[sLength];
...

PreparedStatement sPstmt = connection().prepareStatement("SELECT BLOB_COLUMN FROM BLOB_TABLE FOR UPDATE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    Blob sBlob = sRs.getBlob(1);
    sBlob.setBytes(0, sBytes);
    ...
}
...

BLOB 데이터 읽기#

LOB 데이터를 읽을 때, 내부적으로는 Lob Locator를 이용하기 때문에 반드시 commit 또는 rollback과 같은 명시적인 트랜잭션 종료 작업을 추가해야 한다.

1. InputStream 객체와 getBinaryStream 메소드를 사용한 방법#
...
sCon = getConnection();
PreparedStatement sPstmt = sCon.prepareStatement("SELECT BLOB_COLUMN
FROM BLOB_TABLE");
ResultSet sRs = sPstmt.executeQuery();
while(sRs.next())
{
  ...
  InputStream sInputStream = sRs.getBinaryStream(1);
  ...
}
...
sCon.commit();
2. getBlob 메소드와 InputStream 객체를 사용한 방법#
...
sCon = getConnection();
PreparedStatement sPstmt = sCon.prepareStatement("SELECT BLOB_COLUMN
FROM BLOB_TABLE");
ResultSet sRs = sPstmt.executeQuery();
while(sRs.next())
{
  ...
  Blob sBlob = sRs.getBlob(1);
  InputStream sInputStream = sBlob.getBinaryStream();
  ...
}
...
sCon.commit();
3. getBlob 메소드와 byte 배열을 사용한 방법#
...
final int sReadLength = 100;

sCon = getConnection();
PreparedStatement sPstmt = sCon.prepareStatement("SELECT BLOB_COLUMN FROM BLOB_TABLE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    Blob sBlob = sRs.getBlob(1);
    long sRemains = sBlob.length();
    long sOffset = 0;
    while(sRemains > 0)
    {
        byte[] sReadBytes = sBlob.getBytes(sOffset, sReadLength);
        sRemains -= sReadBytes.length;
        sOffset += sReadBytes.length;
        ...
    }
    ...
}
...
sCon.commit();

BLOB 데이터 변경하기#

Truncation#
Statement sStmt = ...

ResultSet sRs = sStmt.executeQuery("SELECT * FROM t1 FOR UPDATE");

while(sRs.next())
{
    ...
    int sLength = ... ;
    Blob sBlob = sRs.getBlob(2);

    // After executing this method
    // sBlob.length() == sLength
    sBlob.truncate(sLength);
}

...

CLOB 사용하기#

아래는 JDBC 애플리케이션에서 CLOB 데이터를 조작하는 방법을 코드 예제로 보여준다.

PreparedStatement를 통해서 CLOB 데이터 쓰기#

다음은 예제에서 사용되는 테이블을 생성하는 구문이다.

CREATE TABLE TEST_TABLE ( C1 BLOB );
1. Reader 객체와 setCharacterStream 메소드를 사용한 방법#
Reader sReader = ...
long sLength = ...
...
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE
VALUES (?)");
...
sPstmt.setCharacterStream(1, sReader, sLength);
...
sPstmt.execute();
...

JDK 1.5에서는 sPstmt를 AltibasePreparedStatement 타입으로 캐스팅 하면 long 타입의 길이 변수로 정의된 setCharacterStream() 메소드를 호출할 수 있다.

import Altibase.jdbc.driver.AltibasePreparedStatement;
...
((AltibasePreparedStatement)sPstmt).setCharacterStream(1, sReader, sLength);
...
2. Writer 객체와 setCharacterStream 메소드를 사용한 방법#
char[] sBuf = ...

...

PreparedStatement sPstmt = connection().prepareStatement("SELECT * FROM TEST_TABLE FOR UPDATE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    Clob sClob = sPstmt.getClob(1);
    Writer sWriter = sClob.setCharacterStream(1);
    sWriter.write(sBuf);
    sWriter.close();
    ...
}

...

sPstmt.execute();

...
3. Clob 객체와 setClob 메소드를 사용한 방법#
java.sql.Clob sClob = ...
...
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE
VALUES (?)");
...
sPstmt.setClob(1, sClob);
...
sPstmt.execute();
...
4. Clob 객체와 setObject 메소드를 사용한 방법#
java.sql.Clob sClob = ...
...
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE
VALUES (?)");
...
sPstmt.setObject(1, sClob);
...
sPstmt.execute();
...
5. setObject 메소드에 SQL 타입을 지정한 방법#
java.sql.Clob sClob = ...
...
PreparedStatement sPstmt = connection().prepareStatement("INSERT INTO TEST_TABLE
VALUES (?)");
...
sPstmt.setObject(1, sClob, AltibaseTypes.Clob);
...
sPstmt.execute();
...

ResultSet 객체를 사용해서 CLOB 데이터 쓰기#

다음은 예제에서 사용되는 테이블을 생성하는 구문이다.

CREATE TABLE CLOB_TABLE ( CLOB_COLUMN CLOB );
1. Reader 객체와 updateCharacterStream 메소드를 사용한 방법#
Reader sReader = ...
long sLength = ... // The length of source from which Reader is linked

...

PreparedStatement sPstmt = connection().prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateCharacterStream(1, sReader, sLength);
    sRs.updateRow();
    ...
}

...
2. Clob 객체와 updateClob 메소드를 사용한 방법#
java.sql.Clob sClob = ...

...

PreparedStatement sPstmt = connection().prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateClob(1, sClob);
    sRs.updateRow();
    ...
}

...
3. Clob 객체와 updateObject 메소드를 사용한 방법#
java.sql.Clob sClob = ...

...

PreparedStatement sPstmt = connection().prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateObject(1, sClob);
    sRs.updateRow();
    ...
}

...
4. updateObject 메소드에 SQL 타입을 지정한 방법#
java.sql.Clob sClob = ...

...

PreparedStatement sPstmt = connection().prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    sRs.updateObject(1, sClob, AltibaseTypes.CLOB);
    sRs.updateRow();
    ...
}

...

SELECT … FOR UPDATE 구문으로 CLOB 데이터 삽입#

...

String sStr = ... ; 
PreparedStatement sPstmt = connection().prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE FOR UPDATE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    Clob sClob = sRs.getClob(1);
    sClob.setString(0, sStr);
    ...
}

...

CLOB 데이터 읽기#

LOB 데이터를 읽을 때, 내부적으로는 Lob Locator를 이용하기 때문에 반드시 commit 또는 rollback과 같은 명시적인 트랜잭션 종료 작업을 추가해야 한다.

1. Reader 객체와 getCharacterStream 메소드를 사용한 방법#
...
sCon = getConnection();
PreparedStatement sPstmt = sCon.prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    Reader sReader = sRs.getCharacterStream(1);
    ...
}
...
sCon.commit();
2. Reader 객체 와 getClob 메소드를 사용한 방법#
...  
sCon = getConnection();
PreparedStatement sPstmt = sCon.prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    Clob sClob = sRs.getClob(1);
    Reader sReader = sClob.getCharacterStream();
    ...
}
...
sCon.commit();
3. getClob 메소드와 String 객체를 사용한 방법#
...
final int sReadLength = 100;

sCon = getConnection();
PreparedStatement sPstmt = sCon.prepareStatement("SELECT CLOB_COLUMN FROM CLOB_TABLE");

ResultSet sRs = sPstmt.executeQuery();

while(sRs.next())
{
    ...
    Clob sClob = sRs.getClob(1);
    long sRemains = sClob.length();
    long sOffset = 0;
    while(sRemains > 0)
    {
        String sStr = sClob.getSubString(sOffset, sReadLength);
        sRemains -= sStr.length;
        sOffset += sStr.length;
        ...
    }
    ...
}
...
sCon.commit();

CLOB 데이터 변경하기#

Truncation#
Statement sStmt = ...

ResultSet sRs = sStmt.executeQuery("SELECT * FROM t1 FOR UPDATE");

while(sRs.next())
{
    ...
    int sLength = ... ;
    Clob sClob = sRs.getClob(2);

    // After executing this method
    // sClob.length() == sLength
    sClob.truncate(sLength);
}

...

createBlob(), createClob()을 이용한 LOB 사용#

createBlob(), createClob() 메소드를 이용하여 LOB 데이터를 사용하는 방법을 설명한다.

장점#

JDBC 3.0을 준수하는 이전 버전의 JDBC 드라이버는 LOB 객체를 사용하려면 Statement 객체에서 getBlob() 또는 getClob() 메소드를 사용해 LOB 객체를 획득해야 하지만 Altibase 7.3 이상의 JDBC 드라이버는 connection 객체에서 빈 LOB 객체를 생성하여 LOB 데이터를 저장할 수 있어서 사용성이 크게 향상되었다.

JDBC 3.0 을 준수하는 JDBC 드라이버에서 LOB 객체 사용 예

Connection sConn = getConnection();
PreparedStatement sStmt = sConn.prepareStatement("INSERT INTO T1 VALUES (?)");
File sFile = new File(aFileUrl2);
FileReader sFileReader = new FileReader(sFile);
// clob 객체를 바로 사용할 수 없고 setCharacterStream을 사용해야 한다.
sStmt.setCharacterStream(1, sFileReader, (int)sFile.length());   
...
sFile = new File(aFileUrl2);
FileInputStream sFInStream = new FileInputStream(sFile);
// blob 객체를 바로 사용할 수 없고 setBinaryStream을 사용해야 한다.
sStmt.setBinaryStream(1, sFInStream , (int)sFile.length());    
...
sStmt.executeUpdate();
...
// LOB 객체를 사용하려면 먼저 execute를 수행하여 ResultSet으로부터 LOB 객체를 획득해야 한다.
Statement sStmt = sConn.createStatement();
ResultSet sRs = sStmt.executeQuery("SELECT * FROM t1");
Clob sClob = sRs.getClob(1); 
...

Altibase 7.3 이상의 JDBC 드라이버에서 LOB 객체 사용 예

Connection sConn = getConnection();
PreparedStatement sStmt = sConn.prepareStatement("INSERT INTO T1 VALUES (?)");

Clob sClob = sConn.createClob();  // Connection 객체에서 빈 CLOB 객체 생성
String sStr = readFile(blahblah); 
sClob.setString(1, sStr);         // 빈 CLOB 객체에 데이터 저장
sStmt.setClob(1, sClob);          // CLOB 객체를 바로 사용할 수 있음.
... 
Blob sBlob = sConn.createBlob();
byte[] sBytes = getBytesFromFile(); 
sBlob.setBytes(1, sBytes);      // Connection 객체에서 생성한 빈 BLOB 객체에 데이터 저장
sStmt.setBlob(1, sBlob);        // BLOB 객체를 바로 사용할 수 있음.
...
sStmt.executeUpdate();

고려 사항#

createBlob(), createClob()은 데이터를 포함하지 않은 LOB 객체를 생성한다. 이렇게 생성된 LOB 객체에 LOB 데이터를 저장하고 조작하는 것은 JDBC 드라이버에서 관리한다. 따라서 클라이언트의 처리 성능과 메모리 사용에 영향을 줄 수 있다. LOB 데이터 크기에 따라 메모리 부족(OutOfMemory) 에러가 발생할 수도 있으므로 이때는 일반적인 stream 방식으로 LOB 데이터를 처리해야 한다.

또한 createBlob(), createClob()으로 생성한 LOB 객체는 long 데이터형을 지원하지 않는다.

BLOB 데이터 입력 예제#

createBlob() 메소드로 생성한 BLOB 객체와 PreparedStatement.setBlob() 메소드를 사용하여 BLOB 데이터를 입력하는 예제이다.

Connection sConn = getConnection();
java.sql.Blob sBlob = sConn.createBlob(); // createBlob()으로 빈 BLOB 객체를 생성
sBlob.setBytes(...);                      // 빈 BLOB 객체에 BLOB 데이터를 할당
...
PreparedStatement sPstmt = sConn.prepareStatement("INSERT INTO BLOB_TABLE VALUES (?)");
sPstmt.setBlob(1, sBlob);
...
sPstmt.executeUpdate();
sBlob.free();
sPstmt.close();
...

CLOB 데이터 입력 예제#

createClob() 메소드로 생성한 CLOB 객체와 PreparedStatement.setClob() 메소드를 사용하여 CLOB 데이터를 입력하는 예제이다.

Connection sConn = getConnection();
java.sql.Clob sClob = sConn.createClob();  // createClob()으로 빈 CLOB 객체를 생성
sClob.setString(...);                      // 빈 CLOB 객체에 CLOB 데이터를 할당
...
PreparedStatement sPstmt = sConn.prepareStatement("INSERT INTO CLOB_TABLE VALUES (?)");
sPstmt.setClob(1, sClob);
...
sPstmt.executeUpdate();
sClob.free();
sPstmt.close();
...

자원 해제하기#

많은 수의 LOB 객체를 사용해서 데이터를 획득하는 JDBC 애플리케이션의 경우, 반드시 획득한 LOB 객체를 해제하여야 한다. 또한 트랜잭션을 커밋하는 것과 상관없이 LOB 객체는 명시적으로 해제되어야 한다.

아래는 Blob 객체를 해제하는 코드 예제이다.

...
Blob sBlob = sRs.getBlob(1);
// Freeing Lob Locator
((Altibase.jdbc.driver.AltibaseLob)sBlob).free();
...

Blob 객체를 free 메소드로 해제하면, 대응하는 Lob Locator가 서버에서 해제되므로 그 객체에 대해서는 더이상 연산 수행이 불가능하다.

아래는 Clob 객체를 해제하는 코드 예제이다.

...
Clob sClob = sRs.getClob(1);
// Freeing Lob Locator
((Altibase.jdbc.driver.AltibaseLob)sClob).free();
...

Clob 객체도 Blob과 마찬가지로 free 메소드로 해제하면, 대응하는 Lob Locator가 서버에서 해제되므로 그 객체에 대해서는 더 이상 연산 수행이 불가능하다.

아래는 BlobInputStream 객체와 BlobOutputStream 객체를 해제하는 코드 예제이다.

InputStream sInputStream = sRs.getBinaryStream(1);

// Freeing Lob Locator
((Altibase.jdbc.driver.BlobInputStream)sInputStream).freeLocator();


CallableStatement sCallStmt = aConn.prepareCall("INSERT INTO TEST_TABLE VALUES (?)");
sCallStmt.registerOutParameter(1, Types.BLOB);
sCallStmt.execute();

Blob sBlob = sCallStmt.getBlob(1);
OutputStream sOutputStream = sBlob.setBinaryStream(1);

// Freeing Lob Locator
((Altibase.jdbc.driver.BlobOutputStream)sOutputStream).freeLocator();

BlobInputStream 또는 BlobOutputStream 객체를 freeLocator 메소드로 해제하면, 대응하는 Lob Locator가 서버에서 해제되므로 그 객체에 대해서는 더이상 연산 수행이 불가능하다.

아래는 ClobReader 객체와 ClobWriter 객체를 해제하는 코드 예제이다.

Reader sClobReader = sRs.getCharacterStream(1);

// Freeing Lob Locator
((Altibase.jdbc.driver.ClobReader)sClobReader).freeLocator();


CallableStatement sCallStmt = aConn.prepareCall("INSERT INTO TEST_TABLE VALUES (?)");
sCallStmt.registerOutParameter(1, Types.CLOB);
sCallStmt.execute();

Clob sClob = sCallStmt.getClob(1);
Writer sClobWriter = sClob.setCharacterStream(1);

// Freeing Lob Locator
((Altibase.jdbc.driver.ClobWriter)sClobWriter).freeLocator();

ClobReader 또는 ClobWriter 객체를 freeLocator 메소드로 해제하면, 대응하는 Lob Locator가 서버에서 해제되므로 그 객체에 대해서는 더이상 연산 수행이 불가능하다.

제약 사항#

clientside_auto_commit을 on으로 지정하여 JDBC 드라이버가 트랜잭션의 자동커밋을 제어하도록 하여도 LOB 데이터를 다루는 것에는 아래와 같은 제약이 여전히 존재한다.

ResultSet 객체(커서)를 통해 가져온 LOB 데이터를 커서가 닫히기 전에 다른 Statement의 executeUpdate() 메소드에 사용하면 Lob locator가 사라지기 때문에 그 커서로부터 더 이상의 fetch가 불가능해진다. 아래는 이러한 오류 상황이 발생할 수 있는 예제 코드이다.

PreparedStatement sPreStmt =
    sCon.prepareStatement( "INSERT INTO TEST_TEXT " +
                           "VALUES ( ?, ?, ?, ? )" );
Statement sStmt = sCon.createStatement();
ResultSet sRS = sStmt.executeQuery( "SELECT ID, TEXT " +
                                    "  FROM TEST_SAMPLE_TEXT " );
while ( sRS.next() )                // (2)
{
    sID   = sRS.getInt( 1 );
    sClob = sRS.getClob( 2 );
    switch ( sID )
    {
        case 1 :
            sPreStmt.setInt(    1, 1 );
            sPreStmt.setString( 2, "Altibase Greetings" );
            sPreStmt.setClob(   3, sClob );
            sPreStmt.setInt(    4, (int)sClob.length() );
            break;
        case 2 :
            sPreStmt.setInt(    1, 2 );
            sPreStmt.setString( 2, "Main Memory DBMS" );
            sPreStmt.setClob(   3, sClob );
            sPreStmt.setInt(    4, (int)sClob.length() );
            break;
        default :
            break;
    }
    sPreStmt.executeUpdate();       // (1)
}
  • (1): ResultSet sRS가 열려 있는 상태에서 sPreStmt.executeUpdate()를 호출하면, JDBC 드라이버가 트랜잭션을 자동으로 commit하면서 sClob의 Lob locator가 사라진다.

  • (2): Lob locator가 사라졌으므로 sRs.next()에서 exception이 발생할 수 있다.

따라서, 이러한 로직에서 LOB 데이터를 다룰 때는 먼저 setAutoCommit(false)를 호출하여 세션의 자동커밋을 해제해야 한다.

또한, NOT NULL 제약이 있는 LOB 타입 칼럼에 setNull을 수행하면 Unable to insert (or update) NULL into NOT NULL column. 에러가 발생한다. 이 경우, Empty로 초기화된 데이터가 남아 있어 반드시 트랜잭션을 롤백해야 한다.