[Oracle Cloud] SODA Driver for Java を使って見た

# はじめに

[前回](https://qiita.com/sugimount/items/064a1301b461df482f00)の記事では、Oracle Database で JSON データを管理する方法を確認しました。JSON のアクセス方法は、SQL ・ SODA(Simple Oracle Document Access) の2種類あります。
SODA はプログラム開発者向けに用意されている API です。SQLの知識が無くても、JSON ドキュメントを格納・取り出しが出来る特徴があります。

SODA のなかで利用する言語などによって、6種類の方法が提供されています。

- Java
- C
- Rest
- PL/SQL
- Node.js
- Python

https://www.oracle.com/database/technologies/appdev/json.html



今回は、SODA を Java のプログラムから利用するまでの手順を紹介していきます。


# SODA Driver for Java と Table の関係性

と、実際の作業に入る前に、SODA で扱う抽象的な概念と、Oracle Database の Table との関係性を見て行きます。
SODA Driver for Java では、Collection と Document という2つの抽象的な概念を扱い、Oracle Database 上で JSON データをストアできます。Java のコードで見ると、次のような感じです。

```java
            // Create a collection with the name "MyJSONCollection".
            // This creates a database table, also named "MyJSONCollection", to store the
            // collection.
            OracleCollection col = db.admin().createCollection("MyJSONCollection");

            // Create a JSON document.
            OracleDocument doc = db.createDocumentFromString("{ \"name\" : \"Alexander\" }");

            // Insert the document into a collection.
            col.insert(doc);
```



`MyJSONCollection` という名前の Collection を作成し、`{"name" , "Alexander"}` という Document を格納している Java のサンプルコードです。SQL が、ドキュメントとコレクションという概念に隠蔽されていることが分かります。

上記を実行すると、Oracle Database 上では、`MyJSONCollection`  という名前のTableが作成されます。実際のデータはこんな感じです。1個のDocumentが、1個のRow として、Insert されています。

![1598261560011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/3ea0b878-38a1-add0-8342-d229df48799e.png)



Table は5個のカラムで構成されています。

- ID : コレクションで自動的に採番される ID。Primary Key となる。 
- CREATED_ON : Document の作成日時
- LAST_MODIFIED : Document の更新日時
- VERSION : Document のバージョン
- JSON_DOCUMENT : JSON のデータが格納される。BLOB 型に保管される

![1598182994613.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/53e07a8a-0748-91f0-7737-ecdcdda38a7e.png)



Table に設定されている Constraint を見ると、`JSON_DOCUMENT` 列に JSON 用の制約が入っています。

![1598183279203.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/a9c32dec-1f93-823d-3407-474275069d51.png)





なお、自分の環境では、SODA Driver for Java から作成した Table は、SQL 文で直接参照しようとしても、table 名が見つからないエラーが出ました。SQL Developer を使うことで、Table のデータや構造が確認できました。SQL で直接見えない場合は、他のツールを使うと参照出来るように見えます。





# 環境構築手順

それでは、実際に SODA Driver for Java を使って、開発用の環境を構築していきます。SODA for Java Developer's Guide に、必要な前提条件が書かれており、これを参考に作業を進めます。
https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/java/adsda/soda-java-prerequisites.html#GUID-470DC8EE-087E-4196-B3FE-6A24C24C615A

この記事の開発環境は、Visual Studio Code + Maven + Remote Development です。環境に合わせて適宜読み替えてください。

実施する手順

- Remote Development 用の CentOS 7 を構築して、OpenJDK 11 と Maven を Install
- Visual Studio Code の Remote Development で接続
- SODA Driver for Java のダウンロード
- Maven プロジェクトに SODA Driver for Java 追加 (本質的には、クラスパスへ追加)
- Oracle Database のユーザに、SODA 用の権限付与
- Sample プログラム実行



# CentOS 7 構築

CentOS7 上に、OpenJDK・Maven の環境を構築します。既に環境がある場合は、それを使って大丈夫です。
環境が無い場合は、次のURLを参考に適当に構築します。なお、この記事では、Open JDK 11 を インストールしています。
https://qiita.com/sugimount/items/a635473079d837b8944d



# Remote Development + Java Extension Pack

構築した CentOS に遠隔から接続して開発するために、2個の拡張機能を使っています。

- Remote Development で接続
- Java Extension Pack Install

![1598185407871.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/42d85b4a-b05a-59a5-fb91-848e381c58c8.png)



# Download SODA Driver for Java 

Oracle が管理している GitHub で、SODA Driver for Java が公開されています。
https://github.com/oracle/soda-for-java/releases?xd_co_f=262969594c72535bdd71591657038936



CentOS へ wget するために、URLをコピーしておきます。

![1598169211305.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/c50ac1e0-96e0-6908-d72d-f5c0b6c34952.png)




# Install SODA Driver in Maven Project

まずは、Maven で Project を作ります。適当な名前で mkdir します。

```
mkdir -p ~/java
cd ~/java
```



Maven Project 作成 です。`oraclesoda` という名前で作成します。

```
mvn archetype:generate \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-simple \
    -DarchetypeVersion=1.4 \
    -DgroupId=com.example \
    -DartifactId=oraclesoda \
    -Dversion=1.0-SNAPSHOT \
    -Dpackage=com.example
```



作成した Project のなかで、SODA Driver for Java を格納するためにディレクトリを作ります。

```
cd ~/java/oraclesoda
mkdir -p lib/repo/oraclesodadriver/jars
```



SODA Driver for Java を作成したディレクトリに wget でダウンロードします。

```
cd ~/java/oraclesoda/lib/repo/oraclesodadriver/jars
wget https://github.com/oracle/soda-for-java/releases/download/v1.1.4/orajsoda-1.1.4.jar
```



ダウンロードされているか確認です。

```
[opc@soda jars]$ pwd
/home/opc/java/oraclesoda/lib/repo/oraclesodadriver/jars
[opc@soda jars]$ 
[opc@soda jars]$ ls -la
total 452
drwxrwxr-x. 2 opc opc     32 Aug 23 08:07 .
drwxrwxr-x. 3 opc opc     18 Aug 23 08:00 ..
-rw-rw-r--. 1 opc opc 459867 Apr 22 10:59 orajsoda-1.1.4.jar
[opc@soda jars]$ 
```



Maven Project を作成したときに自動生成された、pom.xml の依存関係 (dependencies) の指定で、次の指定をします。

- SODA Driver for Java
- JDBC8
- javax.json

pom.xml の全文を載せます。この中で必要なのは、`<dependencies>` で囲まれている部分です。

```
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>oraclesoda</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>oraclesoda</name>
  <description>A simple oraclesoda.</description>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.11</maven.compiler.source>
    <maven.compiler.target>1.11</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>oracle.soda</groupId>
      <artifactId>sodadriver</artifactId>
      <version>1.4</version>
      <scope>system</scope>
      <systemPath>${basedir}/lib/repo/oraclesodadriver/jars/orajsoda-1.1.4.jar</systemPath>
    </dependency>
    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ojdbc8-production</artifactId>
      <version>19.7.0.0</version>
      <type>pom</type>
    </dependency>
    <dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>javax.json</artifactId>
      <version>1.0.4</version>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

  <reporting>
    <plugins>
      <plugin>
        <artifactId>maven-project-info-reports-plugin</artifactId>
      </plugin>
    </plugins>
  </reporting>
</project>
```



dependencies で指定した、ライブラリ群を download します。Maven Project 用の Directory のなかで、target/dependency にダウンロードされます。
(余談ですが、Oracle の JDBS が Maven として公開されており、楽にダウンロードが出来るようになっています。)
https://www.oracle.com/database/technologies/maven-central-guide.html

```
cd ~/java/oraclesoda
mvn dependency:copy-dependencies
```



ダウンロードされた jar ファイル群の確認です。

```
[opc@soda2 oraclesoda]$ ls -la target/dependency/
total 11280
drwxrwxr-x. 2 opc opc    4096 Aug 23 12:25 .
drwxrwxr-x. 3 opc opc      24 Aug 23 12:25 ..
-rw-rw-r--. 1 opc opc   85147 Aug 23 12:25 javax.json-1.0.4.jar
-rw-rw-r--. 1 opc opc 4398602 Aug 23 12:25 ojdbc8-19.7.0.0.jar
-rw-rw-r--. 1 opc opc    5140 Aug 23 12:25 ojdbc8-production-19.7.0.0.pom
-rw-rw-r--. 1 opc opc  156242 Aug 23 12:25 ons-19.7.0.0.jar
-rw-rw-r--. 1 opc opc  311000 Aug 23 12:25 oraclepki-19.7.0.0.jar
-rw-rw-r--. 1 opc opc 1663954 Aug 23 12:25 orai18n-19.7.0.0.jar
-rw-rw-r--. 1 opc opc  210337 Aug 23 12:25 osdt_cert-19.7.0.0.jar
-rw-rw-r--. 1 opc opc  312200 Aug 23 12:25 osdt_core-19.7.0.0.jar
-rw-rw-r--. 1 opc opc   32168 Aug 23 12:25 simplefan-19.7.0.0.jar
-rw-rw-r--. 1 opc opc  459867 Aug 23 12:25 sodadriver-1.4.jar
-rw-rw-r--. 1 opc opc 1684253 Aug 23 12:25 ucp-19.7.0.0.jar
-rw-rw-r--. 1 opc opc  265130 Aug 23 12:25 xdb-19.7.0.0.jar
-rw-rw-r--. 1 opc opc 1933746 Aug 23 12:25 xmlparserv2-19.7.0.0.jar
[opc@soda2 oraclesoda]$ 

```





# Oracle Database のユーザーに権限付与

Oracle Database で使うユーザーに、SODA_APP という権限を付与します。

```
GRANT SODA_APP TO sugi;

```



実行例

```
SQL> GRANT SODA_APP TO sugi;

Grant succeeded.

SQL>

```



# Sample Code

ここまでの手順で、SODA Driver for Java を使えるようになりました。今回は、SODA を動かしたいだけなので、一旦テストコードは全て削除します。

```
rm -rf ~/java/oraclesoda/src/test/

```



VS Code で、SodaApp.java を言う名前でファイルを新規作成します。

![1598185618694.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/7bdff44d-cc50-41e7-083c-0b264c2affa4.png)



Quick Start なコードを全て載せます。`url`, `user`, `password` などは、環境に合わせて適宜変更してください。

- `MyJSONCollection` 名のコレクションを作成
- 作成したコレクションに、Document を Insert
- Insert する時には、ヒント句で MONITOR を入れており、リアルタイムに実行計画などを参照可能

```java
package com.example;

// import java.sql.Connection;
import java.sql.DriverManager;

import oracle.jdbc.OracleConnection;

import oracle.soda.rdbms.OracleRDBMSClient;

import oracle.soda.OracleDatabase;
import oracle.soda.OracleCursor;
import oracle.soda.OracleCollection;
import oracle.soda.OracleDocument;
import oracle.soda.OracleException;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class SodaApp {
    public static void main(String[] arg) {
        // Set the JDBC connection string, using information appropriate for your Oracle
        // Database instance.
        // (Be sure to replace placeholders host_name, port_number, and service_name in
        // the string.)
        String url = "jdbc:oracle:thin:@//10.0.0.71:1521/pdb01.pubsubnet01.vcn.oraclevcn.com";

        // Set properties user and password.
        // (Be sure to replace placeholders user and password with appropriate string
        // values.)
        Properties props = new Properties();
        props.setProperty("user", "sugi");
        props.setProperty("password", "Sug1_Passw0rd_dayo");

        OracleConnection conn = null;

        try {
            // Get a JDBC connection to an Oracle instance.
            conn = (OracleConnection) DriverManager.getConnection(url, props);

            // Enable JDBC implicit statement caching
            conn.setImplicitCachingEnabled(true);
            conn.setStatementCacheSize(50);

            // Get an OracleRDBMSClient - starting point of SODA for Java application.
            OracleRDBMSClient cl = new OracleRDBMSClient();

            // Get a database.
            OracleDatabase db = cl.getDatabase(conn);

            // Create a collection with the name "MyJSONCollection".
            // This creates a database table, also named "MyJSONCollection", to store the
            // collection.
            OracleCollection col = db.admin().createCollection("MyJSONCollection");

            // Create a JSON document.
            OracleDocument doc = db.createDocumentFromString("{ \"name\" : \"Alexander\" }");

            // Insert the document into a collection.
            // col.insert(doc);
            Map<String, String> hint = new HashMap<String, String>();
            hint.put("hint", "MONITOR");
            col.insertAndGet(doc, hint);

            // Find all documents in the collection.
            OracleCursor c = null;

            try {
                c = col.find().getCursor();
                OracleDocument resultDoc;

                while (c.hasNext()) {
                    // Get the next document.
                    resultDoc = c.next();

                    // Print document components
                    System.out.println("Key:         " + resultDoc.getKey());
                    System.out.println("Content:     " + resultDoc.getContentAsString());
                    System.out.println("Version:     " + resultDoc.getVersion());
                    System.out.println("Last modified: " + resultDoc.getLastModified());
                    System.out.println("Created on:    " + resultDoc.getCreatedOn());
                    System.out.println("Media:         " + resultDoc.getMediaType());
                    System.out.println("\n");
                }
            } finally {
                // IMPORTANT: YOU MUST CLOSE THE CURSOR TO RELEASE RESOURCES.
                if (c != null)
                    c.close();
            }

            // Drop the collection, deleting the table underlying it and the collection
            // metadata.
            if (arg.length > 0 && arg[0].equals("drop")) {
                col.admin().drop();
                System.out.println("\nCollection dropped");
            }
        }
        // SODA for Java throws a checked OracleException
        catch (OracleException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (conn != null)
                    conn.close();
            } catch (Exception e) {
            }
        }
    }
}

```



VS Code 上で、Run を押してプログラムを実行します。

![1598185940535.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/6e368835-68b6-b0b9-aa63-7a20975ae714.png)



実行例

```
[opc@soda2 java]$  cd /home/opc/java ; /usr/lib/jvm/java-11-openjdk-11.0.8.10-0.el7_8.x86_64/bin/java -Dfile.encoding=UTF-8 @/tmp/cp_axz1vz6oaf2zjr4qikomut4ns.argfile com.example.SodaApp 
Key:         B4B652D1424A43BCAEEBA112B2CB80FF
Content:     { "name" : "Alexander" }
Version:     75611B2924EB4168510D33D13DA7D441E65DCB3DEA5F1629A8407161040D7E75
Last modified: 2020-08-23T09:54:42.360941000Z
Created on:    2020-08-23T09:54:42.360941000Z
Media:         application/json


```



Monitor ヒント句を Java で指定しているので、Enterprise Manager で SODA Driver が実行する SQL テキストをそのまま確認できます。
SODA の裏側では、Insert 文が投げられていて、`MyJSONCollection` テーブルにデータが格納されています。



```
insert /*+ MONITOR */ into "SUGI"."MyJSONCollection" ("ID","JSON_DOCUMENT","LAST_MODIFIED","CREATED_ON","VERSION") values (:1 ,:2 ,sys_extract_utc(SYSTIMESTAMP),sys_extract_utc(SYSTIMESTAMP),:3 ) returning "LAST_MODIFIED" into :4 

```



# API Refference

SODA Driver for Java の他のAPIが気になるときは、このURLを確認するとよいでしょう。

JavaDoc
http://oracle.github.io/soda-for-java/

Using SODA for Java
https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/java/adsda/using-soda-java.html#GUID-4A87EFC6-C090-47C3-B025-035DC9F1BFE0



# 参考URL

JSON開発者ガイド
https://docs.oracle.com/cd/F19136_01/adjsn/index.html

SODA Document
https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/index.html

SODAについて
https://www.oracle.com/database/technologies/appdev/json.html

SODAについて
https://orablogs-jp.blogspot.com/2018/10/node-oracledb-30-introduces-soda.html

SODA GitHub
https://github.com/oracle/soda-for-java/

SODA Release Note
https://github.com/oracle/soda-for-java/releases

SODA Java Doc
http://oracle.github.io/soda-for-java/

Oracle JDBC drivers on Maven Central
https://medium.com/oracledevs/all-in-and-new-groupids-oracle-jdbc-drivers-on-maven-central-a76d545954c6

Developers Guide For Oracle JDBC 19.7.0.0 on Maven Central
https://www.oracle.com/database/technologies/maven-central-guide.html

SODAの概要
https://docs.oracle.com/cd/E96517_01/adsdi/overview-soda.html#GUID-BE42F8D3-B86B-43B4-B2A3-5760A4DF79FB