Oracle NoSQL Databaseラージ・オブジェクトAPI

法律上の注意点

Copyright © 2011, 2012, 2013, 2014, Oracle and/or its affiliates.All rights reserved.

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

ここに記載された情報は予告なしに変更される場合がありますまた、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクル社までご連絡ください。

このソフトウェアまたは関連ドキュメントが、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供される場合は、次のNoticeが適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations.As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs.No other rights are granted to the U.S. Government.

このソフトウェアまたはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアまたはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用する際、このソフトウェアまたはハードウェアを安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、オラクル社およびその関連会社は一切の責任を負いかねます。

OracleおよびJavaはOracle およびその関連企業の登録商標です。その他の名称は、他社の商標の可能性があります。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

このソフトウェアまたはハードウェアおよびドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。オラクル社およびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。オラクル社およびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても、一切の責任を負いかねます。

12/5/2014


目次

ラージ・オブジェクトAPIの使用
LOBキー
LOBキー・チェック
LOB API
LOB操作例外
キー/値LOB例
表LOBの例

ラージ・オブジェクトAPIの使用

Oracle NoSQL Databaseは、オーディオやビデオ・ファイルのようなラージ・オブジェクト(LOB)の読取りと書込みに使用可能なインタフェースを備えています。一般に、1MBを超えるオブジェクトはすべてLOBとして表すことができます。LOB APIでは、このようなオブジェクトの読取りと書込みにストリーミングAPIを提供することにより、値全体をマテリアライズ化せずに、大きな値にアクセスできます。

LOBは、基礎となるストレージ・システムに最適なサイズのチャンクの順序として格納されます。1つのLOBを構成するすべてのチャンクが同じサイズとはかぎりません。各チャンクのサイズは、基礎となるストレージ・アーキテクチャとハードウェアの確認に基づいて、自動的に選択されます。LOBを複数のチャンクに分割することにより、混在するワーク・ロード環境で様々なサイズの値を低レイテンシで操作できます。アプリケーションは、ストリーム・ベースのAPIにより、基礎となるストレージ・システムでのLOBの実際の表現に左右されません。

LOBインタフェースでは、表APIまたはキー/値APIとともに使用できるテキストのみのキーを使用します。このドキュメントでは、LOBインタフェースに関連する大まかな概念について説明してから、表APIおよびキー/値APIの両方とともに使用する例を示します。

LOBキー

LOBはoracle.kv.Keyオブジェクトを使用して格納し、取得します。そのような各オブジェクトは、キー・パスを表す一連の文字列を含みます。キー・パスは、メジャーと(オプションで)マイナーとの2つの部分に分かれます。LOBキー内の最後のキー・パス・コンポーネントには、デフォルトで.lobの終了接尾辞を最後に付ける必要があります。

LOB接尾辞はKVStoreConfig.setLOBSuffix()メソッドを使用して定義できます。

Keyオブジェクトを使用して格納される他のオブジェクト(キー/値APIを使用する場合など)とは異なり、LOBデータはキー・パスにあるメジャーおよびマイナーのキー・パス・コンポーネントによって決定されるシャードには格納されません。かわりに、LOBデータは非表示のキースペースを使用し、様々なチャンクは指定したKeyに基づいてではなく、このキースペースに基づいてパーティション全体に渡って配布されます。

また、LOBに使用する実際のキーはメジャー/マイナーのキー・パス・コンポーネントに基づいて単一パーティションに格納され、このストレージに使用されるパーティションは、ストアがキーのメジャーおよびマイナーのキー・パスに基づいてあらゆるデータを分けるのと同じ方法で選択されることに注意してください。ただし、かなり短いキーを前提にすると、これは少量のデータを表し、ストア・データのサイズ要件には実質的に影響しません。

このセクションの残りでは、表APIのユーザーにとっては関心が高い可能性があるOracle NoSQL Database Keyの概要を説明します。キー/値APIユーザーは、すでにこれらの概念を理解していると思われるため、次の項にスキップできます。

Keyオブジェクトは、キー・パス・コンポーネントをどのように表すかに応じていくつかの異なる方法で構成できます。通常、メジャーおよびマイナーのキー・パス・コンポーネントを表すには配列が使用され、これらの配列は次にKey.createKey()メソッドに渡されます。キー・パスの文字列表現を表セルに格納することは簡単なため、表APIのユーザーはKey.fromString()メソッドを便利と感じる場合もあります。あるいは、表APIユーザーはキー・パス・コンポーネントを配列として表セルに格納したり、表セルで見つかる情報を使用してキー・パス配列を作成できます。

文字列として表す場合、キー・パスはスラッシュ('/)で始まり、各パス・コンポーネントの区切りにもスラッシュを使用します。マイナー・キー・パスを使用する場合、メジャー・キー・パスとの間はダッシュ('-)を使用して区切られます。たとえば、イメージ・ファイルに使用するLOBキーは、次のようになります。

/Records/People/-/Smith/Sam/Image.lob

表APIのユーザーは、LOBパスが表の内部で使用するキーと競合しないことを注意して確認する必要があります。表データを格納するために内部で使用するキー・パスは表名の数値表現で始まるため、通常これは難しいことではありません。

LOBキー・チェック

すべてのLOB APIでは、LOBへの接続に使用するキーがこの末尾の接尾辞要件を満たします。終了接尾辞要件を満たしていない場合、LOB APIはIllegalArgumentException例外をスローします。この要件により、非LOBメソッドでは、LOBオブジェクトが誤って変更されないようにチェックできます。

すべてのメソッド(LOBおよび非LOB)に対して実行されるLOB関連のキー・チェックの概要を次に示します。

  • キー/値APIを使用する場合、すべての非LOB書込み操作では、他のキー妥当性チェックの一環として、LOB接尾辞が存在しないことをチェックします。チェックが失敗すると(つまり、キーにLOB接尾辞が含まれていると)非LOB APIはIllegalArgumentExceptionをスローします。

  • キー/値APIを使用する場合、すべての非LOB読取り操作では、内部的に使用される関連付けられた不透明値を返して、LOBストリームを作成します。

  • すべてのLOB書込みおよび読取り操作では、LOB接尾辞が存在することをチェックします。このチェックに失敗すると、IllegalArgumentExceptionがスローされます。

LOB API

LOBはチャンクの順序として表現されるため、LOB APIを使用して排他的にアクセスする必要があります。KVStore.getXXXキー/値メソッドのファミリでLOBキーを使用すると、KVS実装に内部的でアプリケーションからは直接使用できない値を受け取ります。

LOB APIは、KVLargeObjectインタフェースで宣言されています。これはKVStoreのスーパーインタフェースであるため、通常の方法でKVStoreハンドルを作成してから、それを使用してLOBメソッドへアクセスします。

LOBメソッドは次のとおりです。

  • KVLargeObject.deleteLOB()

    ストアからLOBを削除します。

  • KVLargeObject.getLOB()

    ストアからLOBを取得します。

  • KVLargeObject.putLOB()KVLargeObject.putLOBIfAbsent()およびKVLargeObject.putLOBIfPresent()

    ストアにLOBを書き込みます。

LOB操作例外

LOBの読取りと挿入に使用するメソッドは、原子的ではありません。この原子性の要件を解除することにより、ストア全体にLOBチャンクを分散できます。そのため、LOBに対する同時操作はアプリケーションで調整します。LOB実装では、LOBの同時変更を最大限に検出するようにし、競合を検知するとConcurrentModificationExceptionをスローしますが、このような競合がAPIによってすべて検知されることを保証していません。この例外が発生した場合の安全な措置は、例外を引き起こしたアプリケーション・レベルの調整の問題を修正してから、LOBを削除して新しい値で置き換えることです。

LOB変更操作で障害が発生すると、不全LOBが作成されます。不全LOBのLOB値は中間状態であり、アプリケーションによる読取りはできません。不全LOBでのgetLOB()の試行は、PartialLOBExceptionとなります。不完全なputLOB()操作の結果である不全LOBは、操作の再試行によって修復できます。または、削除して、その場所に新しいキー/値を作成できます。不完全な削除操作の結果である不全LOBの場合、削除の再試行を行う必要があります。各LOBメソッドに関連するドキュメントでは、不全LOBにおいて呼び出された場合のそれらの動作について、さらに詳細に説明されています。

キー/値LOBの例

次の例では、キー/値APIを使用して、LOB値を書き込んだ後に読み取ります。オブジェクトがアプリケーション内で実際にマテリアライズ化されることはありませんが、かわりに、値がファイルシステムからストアに直接ストリームされることに注意してください。この簡単な例では、ストアからの読取り時に、取得したバイト数を単純にカウントします。

また、この例では最小限の例外ハンドリングのみを示します。本番コードでは、おそらくこの例で拾う例外を単にレポートするよりも多くを実行すると思います。

package kvstore.lobExample;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import oracle.kv.Consistency;
import oracle.kv.Durability;
import oracle.kv.KVStore;
import oracle.kv.KVStoreConfig;
import oracle.kv.KVStoreFactory;
import oracle.kv.Key;
import oracle.kv.RequestTimeoutException;
import oracle.kv.Version;
import oracle.kv.lob.InputStreamVersion;
import oracle.kv.lob.PartialLOBException;

public class LOBKV {

    private String[] hhosts = {"localhost:5000"};

    public static void main(String args[]) {
        LOBKV lobkv = new LOBKV();
        lobkv.run(args);
        System.out.println("All done.");
    }
    private void run(String args[]) {

        KVStoreConfig kconfig = new KVStoreConfig("kvstore", hhosts);
        KVStore kvstore = KVStoreFactory.getStore(kconfig);

        // Use key "/test/lob/1.lob" to store the jpeg object.
        // Note that we are not using a minor key in this
        // example. As required, the final key component 
        // uses a ".lob" suffix.
        final Key key = 
            Key.createKey(Arrays.asList("test", "lob", "1.lob"));

        File lobFile = new File("test.jpg");
        try {
            FileInputStream fis  = new FileInputStream(lobFile);

            // The image file is streamed from the filesystem into 
            // the store without materializing it within the
            // application. A medium level of durability is
            // used for this put operation. A timeout value
            // of 5 seconds is set in which each chunk of the LOB
            // must be written, or the operation fails with a
            // RequestTimeoutException.
            kvstore.putLOB(key, fis,
                    Durability.COMMIT_WRITE_NO_SYNC,
                    5, TimeUnit.SECONDS);


            // Now read the LOB. It is streamed from the store, 
            // without materialization within the application code. 
            // Here, we only count the number of bytes retrieved.
            //
            // We use the least stringent consistency policy 
            // available for the read. Each LOB chunk must be read 
            // within a 5 second window or a RequestTimeoutException
            // is thrown.
            InputStreamVersion istreamVersion =
                 kvstore.getLOB(key,
                              Consistency.NONE_REQUIRED,
                              5, TimeUnit.SECONDS);

             InputStream stream = istreamVersion.getInputStream();
             int byteCount = 0;
             while (stream.read() != -1) {
                 byteCount++;
             }
             System.out.println(byteCount);
        } catch (FileNotFoundException fnf) {
            System.err.println("Input file not found.");

            System.err.println("FileNotFoundException: " +
                    fnf.toString());
            fnf.printStackTrace();
            System.exit(-1);
        } catch (RequestTimeoutException rte) {
            System.err.println("A LOB chunk was either not read or");
            System.err.println("not written in the alloted time.");

            System.err.println("RequestTimeoutException: " + 
                rte.toString());
            rte.printStackTrace();
            System.exit(-1);
        } catch (PartialLOBException ple) {
            System.err.println("Retrieval (getLOB()) only retrieved");
            System.err.println("a portion of the requested object.");

            System.err.println("PartialLOBException: " + ple.toString());
            ple.printStackTrace();
            System.exit(-1);
        } catch (IOException e) {
            System.err.println("IO Exception: " + e.toString());
            e.printStackTrace();
            System.exit(-1);
        }
    }

    protected LOBKV() {}
} 

表LOBの例

次の例では、表APIを使用して、LOB値を書き込んだ後に読み取ります。オブジェクトがアプリケーション内で実際にマテリアライズ化されることはありませんが、かわりに、値がファイルシステムからストアに直接ストリームされることに注意してください。この簡単な例では、ストアからの読取り時に、取得したバイト数を単純にカウントします。

表APIでLOBを使用する場合でも、LOBオブジェクトを識別するためにKeyを使用する必要があります。つまり、表の行に直接LOBを格納できません。通常、表に格納されている情報を使用してKeyを作成します。たとえば、表セルの1つにLOBキーを単にテキスト文字列として格納できます。または、キーの値を配列(または、マイナー・キー・コンポーネントを使用している場合には2つの配列)として表セルに格納できます。最後に、行の1つ以上のセルから取得した値に基づいてキーを作成できます。

また、この例では最小限の例外ハンドリングのみを示します。本番コードでは、おそらくこの例で拾う例外を単にレポートするよりも多くを実行すると思います。

始める前に、ストアにある表を定義および作成する必要があります。次の表定義ではユーザー情報のとても小さな表を説明します。次に、ユーザーに関連付けられている1つ以上のイメージ・ファイルを識別するために子表を使用します。

table create -name userTable
add-field -type STRING -name userid
add-field -type STRING -name familiarname
add-field -type STRING -name surname
primary-key -field userid -field familiarname -field surname
shard-key -field userid
exit
plan add-table -name userTable -wait

table create -name userTable.images
add-field -type STRING -name imageFileName
add-field -type STRING -name imageVersion
add-field -type STRING -name imageDescription
primary-key -field imageFileName
exit
plan add-table -name userTable.images -wait

ストアに表定義を追加します。

> java -Xmx256m -Xms256m \
-jar KVHOME/lib/kvstore.jar runadmin -host <hostName> \
-port <port> -store <storeName>
kv-> load -file createLOBTable.txt
Table userTable built.
Executed plan 5, waiting for completion...
Plan 5 ended successfully
Table userTable.images built.
Executed plan 6, waiting for completion...
Plan 6 ended successfully

ここまでで表データへの書込みおよび表データからの読取りができるようになりました。次の例では、ユーザー間に3つの関連イメージが存在する2つのユーザーを作成します。最初に表の行が作成(書込み)され、次にBLOBデータがストアに保存されます。例では、次に表全体を反復して関連情報を表示し、途中で各ユーザーに関連付けられているイメージも表示します。この例では、BLOB表示をBLOBのバイト数の単純なレポートのみに限定します。

package kvstore.lobExample;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import oracle.kv.Consistency;
import oracle.kv.Durability;
import oracle.kv.KVStore;
import oracle.kv.KVStoreConfig;
import oracle.kv.KVStoreFactory;
import oracle.kv.Key;
import oracle.kv.RequestTimeoutException;
import oracle.kv.Version;
import oracle.kv.lob.InputStreamVersion;
import oracle.kv.lob.PartialLOBException;

import oracle.kv.table.PrimaryKey;
import oracle.kv.table.Row;
import oracle.kv.table.Table;
import oracle.kv.table.TableAPI;
import oracle.kv.table.TableIterator;
import oracle.kv.table.MultiRowOptions;

public class LOBTable {

    private String[] hhosts = {"localhost:5000"};

    // Store handles
    private KVStoreConfig kconfig;
    private KVStore kvstore;

    // Table handles
    private TableAPI tableH;
    private Table userTable;
    private Table userImageTable;
    private Row row;

    private static String blobPfx = "blobpfx";
    private static String imgSfx = "userimage.lob";

    public static void main(String args[]) {
        LOBTable lobtable = new LOBTable();
        lobtable.run(args);
        System.out.println("All done.");
    }

    private void run(String args[]) {

        // Open a store handle
        kconfig = new KVStoreConfig("kvstore", hhosts);
        kvstore = KVStoreFactory.getStore(kconfig);
        tableH = kvstore.getTableAPI();


        // Obtain table handles
        userTable = tableH.getTable("userTable");
        userImageTable = tableH.getTable("userTable.images");

        // populate the tables, and load LOBs into the store
        addData();

        // retrieve tables, and retrieve LOBs from the store
        // and show some details about the tables and LOBs.
        retrieveData();
    }

    // Creates some table rows and loads images into the store
    private void addData() {

        // Load up a couple of rows in the user (parent) table.
        row = userTable.createRow();
        row.put("userid","m.beckstrom.3267");
        row.put("familiarname","Mary");
        row.put("surname","Beckstrom");
        tableH.put(row, null, null);

        row = userTable.createRow();
        row.put("userid","h.zwaska.9140");
        row.put("familiarname","Harry");
        row.put("surname","Zwaska");
        tableH.put(row, null, null);

        // Now populate each row's image (child) table
        // and stream in a BLOB as each row is created.
        row = userImageTable.createRow();
        row.put("userid","m.beckstrom.3267");
        row.put("imageFileName","IMG_2581.jpg");
        row.put("imageDescription","Highrise sunset");
        tableH.put(row, null, null);
        loadBlob("m.beckstrom.3267", "IMG_2581.jpg");

        row = userImageTable.createRow();
        row.put("userid","m.beckstrom.3267");
        row.put("imageFileName","IMG_2607.jpg");
        row.put("imageDescription","Swing set at dawn");
        tableH.put(row, null, null);
        loadBlob("m.beckstrom.3267", "IMG_2607.jpg");

        row = userImageTable.createRow();
        row.put("userid","h.zwaska.9140");
        row.put("imageFileName","mom1.jpg");
        row.put("imageDescription","Mom's 89th birthday");
        tableH.put(row, null, null);
        loadBlob("h.zwaska.9140", "mom1.jpg");
    }

    // Loads a blob of data into the store
    private void loadBlob(String userid, String filename) {

        // Construct the key.
        // userid and filename are information saved in the
        // table, so later we can recreate the key by table
        // examination. blobPfx is a constant that we use for 
        // all BLOB data. imgSfx ends the key path with the
        // required suffix. We use a fixed constant partially
        // to meet the BLOB suffix requirement, but in a 
        // larger system this could also be used to 
        // differentiate the type of data contained in the 
        // BLOB (image data versus an audio file, for example).

        final Key key = Key.createKey(
                Arrays.asList(blobPfx, userid, filename, imgSfx));

        File lobFile = new File(filename);
        try {
            FileInputStream fis  = new FileInputStream(lobFile);
            // The image file is streamed from the filesystem into 
            // the store without materializing it within the
            // application. A medium level of durability is
            // used for this put operation. A timeout value
            // of 5 seconds is set in which each chunk of the LOB
            // must be written, or the operation fails with a
            // RequestTimeoutException.
            kvstore.putLOB(key, fis,
                    Durability.COMMIT_WRITE_NO_SYNC,
                    5, TimeUnit.SECONDS);
        } catch (FileNotFoundException fnf) {
            System.err.println("Input file not found.");

            System.err.println("FileNotFoundException: " +
                    fnf.toString());
            fnf.printStackTrace();
            System.exit(-1);
        } catch (RequestTimeoutException rte) {
            System.err.println("A LOB chunk was either not read or");
            System.err.println("not written in the alloted time.");

            System.err.println("RequestTimeoutException: " + 
                rte.toString());
            rte.printStackTrace();
            System.exit(-1);
        } catch (IOException e) {
            System.err.println("IO Exception: " + e.toString());
            e.printStackTrace();
            System.exit(-1);
        }
    }

    // Retrieves the user (parent) table, as well as the images
    // (child) table, and then iterates over the user table, 
    // displaying each row as it is retrieved.
    private void retrieveData() {

        PrimaryKey key = userTable.createPrimaryKey();
        // Iterate over every row in the user table including
        // images (child) table in the result set.
        MultiRowOptions mro = new MultiRowOptions(null, null,
                Arrays.asList(userImageTable));
        TableIterator<Row> iter = 
                   tableH.tableIterator(key, mro, null);
        try {
            while (iter.hasNext()) {
                Row row = iter.next();
                displayRow(row);
            }
        } finally {
            iter.close();
        }
    }

    // Display a single table row. Tests to see if the
    // table row belongs to a user table or a user images
    // table, and then displays the row appropriately.
    private void displayRow(Row row) {
        if (row.getTable().equals(userTable)) {
            System.out.println("\nName: " +
                    row.get("familiarname").asString().get() +
                    " " +
                    row.get("surname").asString().get());
            System.out.println("UID: " +
                    row.get("userid").asString().get());
        } else if (row.getTable().equals(userImageTable)) {
            showBlob(row);
        }
    }

   // Retrieves and displays a BLOB of data. For this limited
    // example, the BLOB display is limited to simply reporting
    // on its size.
    private void showBlob(Row row) {
        // Build the blob key based on information stored in the
        // row, plus external constants.
        String userid = row.get("userid").asString().get();
        String filename  = row.get("imageFileName").asString().get();
        final Key key = Key.createKey(
                Arrays.asList(blobPfx, userid, filename, imgSfx));

        // Show supporting information about the file which we have
        // stored in the table row:
        System.out.println("\n\tFile: " + filename);
        System.out.println("\tDescription: " +
                row.get("imageDescription").asString().get());

        try {
            // Now read the LOB. It is streamed from the store, 
            // without materialization within the application code. 
            // Here, we only count the number of bytes retrieved.
            //
            // We use the least stringent consistency policy 
            // available for the read. Each LOB chunk must be read 
            // within a 5 second window or a RequestTimeoutException
            // is thrown.
            InputStreamVersion istreamVersion =
                 kvstore.getLOB(key,
                              Consistency.NONE_REQUIRED,
                              5, TimeUnit.SECONDS);

             InputStream stream = istreamVersion.getInputStream();
             int byteCount = 0;
             while (stream.read() != -1) {
                 byteCount++;
             }
             System.out.println("\tBLOB size: " + byteCount);

        } catch (RequestTimeoutException rte) {
            System.err.println("A LOB chunk was either not read or");
            System.err.println("not written in the alloted time.");

            System.err.println("RequestTimeoutException: " + 
                rte.toString());
            rte.printStackTrace();
            System.exit(-1);
        } catch (PartialLOBException ple) {
            System.err.println("Retrieval (getLOB()) only retrieved");
            System.err.println("a portion of the requested object.");

            System.err.println("PartialLOBException: " + ple.toString());
            ple.printStackTrace();
            System.exit(-1);
        } catch (IOException e) {
            System.err.println("IO Exception: " + e.toString());
            e.printStackTrace();
            System.exit(-1);
        }
    }

    protected LOBTable() {}
}