7 JSONデータにLOB記憶域を使用する際の考慮事項

クライアントを使用してJSONデータをLOBインスタンスとして取得する場合の考慮事項など、JSONデータのLOB記憶域に関する考慮事項について説明します。

一般的な考慮事項

JSONデータにLOB記憶域を使用する場合は、CLOB記憶域ではなくBLOBを使用することをお薦めします。

これは、データベースの文字セットがOracleの推奨値であるAL32UTF8に設定されている場合に、特に該当します。AL32UTF8のデータベースでは、CLOBインスタンスはUCS2文字セットを使用して格納されます。これは、1文字に2バイト必要になることを意味します。ある文書の内容の大半が、文字セットAL32UTF8でシングルバイトを使用して表される文字で構成されている場合は、この文書に必要な記憶域が2倍になります。

データベースの文字セットがAL32UTF8ではない場合でも、CLOBではなくBLOBの記憶域を選択すると、JSON文書を格納するときに文字セットの変換が必要なくなるという利点があります(JSONデータの文字セットおよび文字エンコーディングを参照)。

ラージ・オブジェクト(LOB)を使用する場合は、次のようにすることをお薦めします。

  • LOB (COLUMN_NAME) STORE AS (CACHE)CREATE TABLE文で使用して、JSON文書の読取り操作が、データベース・バッファ・キャッシュを使用して最適化されるようにします。

  • SecureFiles LOBを使用します。

SQL/JSONファンクションおよび条件では、データが格納されるのがBLOBCLOBかという特別な考慮事項なしに、JSONデータを使用できます。アプリケーション開発の観点から見ると、BLOBコンテンツと連携するためのAPIコールは、CLOBコンテンツと連携するためのものとほとんど同じです。

CLOBではなくBLOB記憶域を選択する(JSONまたはその他の種類のデータを対象に)マイナス面は、SQL*Plusなどのコマンドライン・ツールを使用したBLOBコンテンツとの連携がより難しい場合があるということです。次に例を示します。

  • BLOB列からデータを選択するときに、出力可能なテキストとしてそのデータを表示する場合に、SQLファンクションto_clobを使用する必要がある。

  • BLOB列を対象に挿入または更新の操作を実行するときに、SQLファンクションrawtohexを使用して、文字列を明示的にBLOB形式に変換する必要がある。Foot 1

関連項目:

クライアントを使用してJSONデータをLOBインスタンスとして取得する場合の考慮事項

Oracle Call Interface (OCI)やJava Database Connectivity (JDBC)などのクライアントを使用してデータベースからJSONデータを取得する場合は、次の考慮事項を適用してください。

クライアントがデータベースからJSONデータを含むLOBを取得するには、主に次の3つの方法があります。

  • SQL/JSON操作によって戻されたLOBロケータによる、LOBロケータ・インタフェースの使用脚注2

  • LOBデータ・インタフェースの使用

  • LOBコンテンツの直接読取り

一般には、LOBデータ・インタフェースを使用するか、コンテンツを直接読み取ることをお薦めします。

LOBロケータ・インタフェースを使用する場合は、次の点に留意してください。

  • LOBは一時的かつ読取り専用であることに注意してください。

  • 次の行をフェッチする前に、現在のLOBコンテンツを完全に読み取ってください。次の行をフェッチすると、このコンテンツは読取り不可能になります。

    次の行をフェッチした後もクライアントでこの現在のLOBコンテンツが引き続き必要な場合は、このコンテンツをメモリーまたはディスクに保存します。

  • 各行の読取り後に、フェッチしたLOBロケータを解放してください。そうしないと、パフォーマンスが低下し、メモリーがリークする可能性があります。

LOBロケータ・インタフェースを使用する場合は、次の最適化も考慮してください。

  • フェッチに必要なラウンドトリップ回数を最小限に抑えるために、LOBプリフェッチ・サイズを256 KBなどの大きい値に設定します。

  • バッチ・フェッチ・サイズを1000行などの大きい値に設定します。

例7-1および例7-2は、JDBCでLOBロケータ・インタフェースを使用する方法を示しています。

例7-3および例7-4は、ODP.NETでLOBロケータ・インタフェースを使用する方法を示しています。

これらの各例では、一度に1つのLOB行をフェッチします。次の行のフェッチ後も現在のLOBコンテンツを読取り可能な状態に保つために、コンテンツ全体も読み取ります。

LOBデータ・インタフェースを使用する場合は、次の点に留意してください。

  • OCIでは、BLOBデータおよびCLOBデータに対してデータ型SQLT_BINおよびSQLT_CHRをそれぞれ使用します。

  • JDBCでは、BLOBおよびCLOBデータに対してデータ型LONGVARBINARYおよびLONGVARCHARをそれぞれ使用します。

例7-5および例7-6は、JDBCでLOBデータ・インタフェースを使用する方法を示しています。

例7-7および例7-8は、JDBCを使用してLOBコンテンツ全体を直接読み取る方法を示しています。

例7-9および例7-10は、ODP.NETを使用してLOBコンテンツ全体を直接読み取る方法を示しています。

例7-1 JDBCクライアント: LOBロケータ・インタフェースを使用したJSON BLOBデータの取得


static void test_JSON_SERIALIZE_BLOB() throws Exception {
  try(
      OracleConnection conn = getConnection();
      OracleStatement stmt = (OracleStatement)conn.createStatement();
      ) {
    stmt.setFetchSize(1000); // Set batch fetch size to 1000 rows.

    // Set LOB prefetch size to be 256 KB.
    ((OraclePreparedStatement)stmt).setLobPrefetchSize(256000);

    // Query the JSON data in column jblob of table myTab1,
    // serializing the returned JSON data as a textual BLOB instance.
    String query =
      "SELECT json_serialize(jblob RETURNING BLOB) FROM myTab1";
    ResultSet rs = stmt.executeQuery(query);

    while(rs.next()) { // Iterate over the returned rows.
      Blob blob = rs.getBlob(1);

      // Do something with the BLOB instance for the row...

      // Read full content, to be able to access past current row.
      String val =
        new String(blob.getBytes(1,
                                 (int)blob.length()),
                                 StandardCharsets.UTF_8);

      // Free the LOB at the end of each iteration.
      blob.free();
    }
    rs.close();
    stmt.close();
  }
}

例7-2 JDBCクライアント: LOBロケータ・インタフェースを使用したJSON CLOBデータの取得

static void test_JSON_SERIALIZE_CLOB() throws Exception {
  try(
      OracleConnection conn = getConnection();
      OracleStatement stmt = (OracleStatement)conn.createStatement();
      ){
    stmt.setFetchSize(1000); // Set batch fetch size to 1000 rows.

    // Set LOB prefetch size to be 256 KB.
    ((OraclePreparedStatement)stmt).setLobPrefetchSize(256000);

    // Query the JSON data in column jclob of table myTab2,
    // serializing the returned JSON data as a textual CLOB instance.
    String query =
      "SELECT json_serialize(jclob RETURNING CLOB) FROM myTab2";
 
    ResultSet rs = stmt.executeQuery(query);

    while(rs.next()) { // Iterate over the returned rows.
      Clob clob = rs.getClob(1);

      // Do something with the CLOB instance for the row...

      // Read full content, to be able to access past current row.
      String val = clob.getSubString(1, (int)clob.length());

      // Free the LOB at the end of each iteration.
      blob.free();
    }
    rs.close();
    stmt.close();
  }
}

例7-3 ODP.NETクライアント: LOBロケータ・インタフェースを使用したJSON BLOBデータの取得

static void test_JSON_SERIALIZE_BLOB()
{
  try
  {
    using (OracleConnection conn =
      new OracleConnection(
        "user id=<schema>;password=<password>;data source=oracle"))
    {
      conn.Open();
      OracleCommand cmd = conn.CreateCommand();

      // Set LOB prefetch size to be 256 KB.
      cmd.InitialLOBFetchSize = 256000;

      // Query the JSON datatype data in column jblob of table myTab1,
      // serializing the returned JSON data as a textual BLOB instance.
      cmd.CommandText =
        "SELECT json_serialize(jblob RETURNING BLOB) FROM myTab1";

      OracleDataReader rs = cmd.ExecuteReader();

      // Iterate over the returned rows.
      while (rs.Read())
      {
        OracleBlob blob = rs.GetOracleBlob(0);

        // Do something with the BLOB instance for the row...

        // Read full content, to be able to access past current row.
        String val = Encoding.UTF8.GetString(blob.Value);


        blob.Close();
        blob.Dispose();
      }
      rs.Close();
    }
  }
  catch (Exception e)
  {
    throw e;
  }
}

例7-4 ODP.NETクライアント: LOBロケータ・インタフェースを使用したJSON CLOBデータの取得

static void test_JSON_SERIALIZE_CLOB()
{
  try
  {
    using (OracleConnection conn =
      new OracleConnection(
        "user id=<schema>;password=<password>;data source=oracle"))
    {
      conn.Open();
      OracleCommand cmd = conn.CreateCommand();

      // Set LOB prefetch size to be 256 KB.
      cmd.InitialLOBFetchSize = 256000;

      // Query the JSON datatype data in column jclob of table myTab2,
      // serializing the returned JSON data as a textual CLOB instance.
      cmd.CommandText =
        "SELECT json_serialize(jclob RETURNING CLOB) FROM myTab2";

      OracleDataReader rs = cmd.ExecuteReader();

      // Iterate over the returned rows.
      while (rs.Read())
      {
        OracleClob clob = rs.GetOracleClob(0);

        // Do something with the CLOB instance for the row...

        // Read full content, to be able to access past current row.
        String val = clob.Value;


        clob.Close();
        clob.Dispose();
      }
      rs.Close();
    }
  }
  catch (Exception e)
  {
    throw e;
  }
}

例7-5 JDBCクライアント: LOBデータ・インタフェースを使用したJSON BLOBデータの取得

static void test_JSON_SERIALIZE_LONGVARBINARY() throws Exception {
  try(
      OracleConnection conn = getConnection();
      OracleStatement  stmt = (OracleStatement)conn.createStatement();
      ){

    // Query the JSON data in column jblob of table myTab1,
    // serializing the returned JSON data as a textual BLOB instance.
    String query =
      "SELECT json_serialize(jblob RETURNING BLOB) FROM myTab1";
    stmt.defineColumnType(1, OracleTypes.LONGVARBINARY, 1);
    ResultSet rs = stmt.executeQuery(query);
 
    while(rs.next()) { // Iterate over the returned rows.
      BufferedReader br =
        new BufferedReader(
              new InputStreamReader(rs.getBinaryStream( 1 )));
      int size = 0;
      int data = 0;
      data = br.read();
      while( -1 != data ){
          System.out.print( (char)(data) );
        data = br.read();
        size++;
      }
      br.close();
    }
    rs.close();
    stmt.close();
  }
}

例7-6 JDBCクライアント: LOBデータ・インタフェースを使用したJSON CLOBデータの取得

static void test_JSON_SERIALIZE_LONGVARCHAR() throws Exception {
  try(
      OracleConnection conn = getConnection();
      OracleStatement  stmt = (OracleStatement)conn.createStatement();
      ){

    // Query the JSON data in column jclob of table myTab2,
    // serializing the returned JSON data as a textual CLOB instance.
    String query =
      "SELECT json_serialize(jclob RETURNING CLOB) FROM myTab2";
    stmt.defineColumnType(1, OracleTypes.LONGVARCHAR, 1);
    ResultSet rs = stmt.executeQuery(query);
 
    while(rs.next()) { // Iterate over the returned rows.
      Reader reader = rs.getCharacterStream(1);
      int size = 0;
      int data = 0;
      data = reader.read();
      while( -1 != data ){
        System.out.print( (char)(data) );
        data = reader.read();
        size++;
      }
      reader.close();
    }
    rs.close();
    stmt.close();
  }
}

例7-7 JDBCクライアント: getBytesを使用したBLOBコンテンツ全体の直接読取り

static void test_JSON_SERIALIZE_BLOB_2() throws Exception {
  try(
      OracleConnection con = getConnection();
      OracleStatement stmt = (OracleStatement)con.createStatement();
      ){
    stmt.setFetchSize(1000); // Set batch fetch size to 1000 rows.

    // set LOB prefetch size to be 256 KB.
    ((OracleStatement)stmt).setLobPrefetchSize(256000);

    // Query the JSON data in column jblob of table myTab1,
    // serializing the returned JSON data as a textual BLOB instance.
    String query =
      "SELECT json_serialize(jblob RETURNING BLOB) FROM myTab1";
    ResultSet rs = stmt.executeQuery(query);

    while(rs.next()) { // Iterate over the returned rows.
      String val = new String(rs.getBytes(1), StandardCharsets.UTF_8);
    }
    rs.close();
    stmt.close();
    }
  }

例7-8 JDBCクライアント: getStringを使用したCLOBコンテンツ全体の直接読取り

static void test_JSON_SERIALIZE_CLOB_2() throws Exception {
  try(
      OracleConnection conn = getConnection();
      OracleStatement  stmt = (OracleStatement)conn.createStatement();
      ){
    stmt.setFetchSize(1000); // Set batch fetch size to 1000 rows.

    // Set LOB prefetch size to be 256 KB.
    ((OracleStatement)stmt).setLobPrefetchSize(256000);

    // Query the JSON data in column jclob of table myTab2,
    // serializing the returned JSON data as a textual CLOB instance.
    String query =
      "SELECT json_serialize(jclob RETURNING CLOB) FROM myTab2";
    ResultSet rs = stmt.executeQuery(query);

    while(rs.next()) { // Iterate over the returned rows.
      String val = rs.getString(1);
    }
    rs.close();
    stmt.close();
    }
  }

例7-9 ODP.NETクライアント: getBytesを使用したBLOBコンテンツ全体の直接読取り

static void test_JSON_SERIALIZE_BLOB_2()
{
  try
  {
    using (OracleConnection conn = new OracleConnection("user id=scott;password=tiger;data source=oracle"))
    {
      conn.Open();
      OracleCommand cmd = conn.CreateCommand();

      // Set LOB prefetch size to be 256 KB.
      cmd.InitialLOBFetchSize = 256000;

      // Query the JSON datatype data in column blob of table myTab1,
      // serializing the returned JSON data as a textual BLOB instance.

      cmd.CommandText =
        "SELECT json_serialize(blob RETURNING BLOB) FROM myTab1";
      OracleDataReader rs = cmd.ExecuteReader();

      // Iterate over the returned rows.
      while (rs.Read())
      {
        long len = rs.GetBytes(0, 0, null, 0, 0); /* Get LOB length */
        byte[] obuf = new byte[len];
        rs.GetBytes(0, 0, obuf, 0, (int)len);
        String val = Encoding.UTF8.GetString(obuf);
      }
      rs.Close();
    }
  }
  catch (Exception e)
  {
    throw e;
  }
}

例7-10 ODP.NETクライアント: getStringを使用したCLOBコンテンツ全体の直接読取り

static void test_JSON_SERIALIZE_CLOB_2()
{
  try
  {
    using (OracleConnection conn =
      new OracleConnection(
        "user id=<schema>;password=<password>;data source=oracle"))
    {
      conn.Open();
      OracleCommand cmd = conn.CreateCommand();

      // Set LOB prefetch size to be 256 KB.
      cmd.InitialLOBFetchSize = 256000;

      // Query the JSON datatype data in column clob of table myTab2,
      // serializing the returned JSON data as a textual CLOB instance.

      cmd.CommandText = "SELECT json_serialize(clob RETURNING CLOB) FROM myTab2";

      OracleDataReader rs = cmd.ExecuteReader();

      // Iterate over the returned rows.
      while (rs.Read())
      {
        String val = rs.GetString(0);
      }
      rs.Close();
    }
  }
  catch (Exception e)
  {
    throw e;
  }
}


脚注の凡例

脚注1: SQLファンクションrawtohexの戻り値は、32767バイトに制限されます。この長さを超える変換済データを削除するために値が切り捨てられます。
脚注2: LOBロケータを戻すことができるSQL/JSONファンクションは、RETURNING CLOBまたはRETURNING BLOBで使用する場合、json_serializejson_valuejson_queryjson_tablejson_arrayjson_objectjson_arrayaggおよびjson_objectaggです。