Image
Top
Navigation

Java APIを使ってDocument Dataベースを触ってみよう!

ここでは、JavaからOrientDBをドキュメント指向データベースとしての操作してみたいと思います。
全体の流れとしては、データベースへの接続、クラスの作成(スキーマの操作)、リレーションの作成といった流れをサンプルプログラムを間に鋏ながら進めていきます。

1.準備

まずは準備ということで必要なるライブラリの設定をします。

Document APIを使用するためには、以下のライブラリにクラスパスを通す必要があります。

orient-commons-*.jar
orientdb-core-*.jar

Java APIからリモートでOrientDBに接続する場合は以下のjarも必要です。

orientdb-client-*.jar
orientdb-enterprise-*.jar

2. データベースへ接続

それでは早速データベースへ接続してみましょう。

リモート接続

今回はIPアドレスが172.16.251.205の OrientDB サーバー上にある DocumentTest データベースへ接続してみます。

ODatabaseDocumentTx database =
  new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest");
  database.open("root", "root");

method chainで書いても問題ありません。


ODatabaseDocumentTx db = 
 new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest").open("root", "root");

上記のOpenメソッドの引数はユーザーIDとパスワードです。
これは、%orientdb%/config/orientdb-server-config.xmlの中に格納されており、今回はデフォルトで登録されているrootユーザを使用しています。

<user name="root" password="AA601EC532268468BFD4B83F5D7D1" resources="*"/>

rootユーザーはデフォルトではサーバーを起動するたびに変更されますので注意しましょう。

ローカル接続

データベースへの接続の別パターンとしてローカルに接続してかつ新たにデータベースを作成したい場合は以下の用に書きます。

ODatabaseDocumentTx db = 
  new DatabaseDocumentTx("plocal:/tmp/databases/DocumentTest").create();

これでデータベースへの接続は完了です。

3. Documentの登録

非常に簡単な例ですが、PostOfficeというクラスを作成して、そちらにnameとaddressというフィールを作成しつつ値をセットしています。

Schema-lessなレコードの登録

    ODocument doc = new ODocument("PostOffice");
    doc.field("name", "東京中央郵便局");
    doc.field("address", "東京都千代田区丸の内2-7-2");

スキーマレスって感じですね。

Schemaの設定

事前にスキーマを設定しておく場合は、以下のように書きます。

    OClass postofficeClass = db.getMetadata().getSchema().createClass("PostOffice");
    postofficeClass.createProperty("id", OType.INTEGER);
    postofficeClass.createProperty("name", OType.STRING);
    postofficeClass.createProperty("address", OType.STRING);

createPropertyメソッドを使い第一引数にフィールド名、第2引数に型を指定します。
指定可能な型の一覧はこちらです。

Schema-Full

事前にスキーマを設定したからといってSchema-Fullになるわけではありません。
クラスに対してsetStrictMode(true)を設定する必要があります。

    OClass postofficeClass = db.getMetadata().getSchema().createClass("PostOffice");
    postofficeClass.createProperty("id", OType.INTEGER);
    postofficeClass.createProperty("name", OType.STRING);
    postofficeClass.createProperty("address", OType.STRING);

        // schema-full へ変更
    postofficeClass.setStrictMode(true);

Schema-Fullの状態でプロパティを変更使用とした場合は以下の例外が発生します

Exception in thread "main" com.orientechnologies.orient.core.exception.OValidationException: 
Found additional field 'hoge'. It cannot be added because the schema class 'PostOffice' is defined as STRICT
    at com.orientechnologies.orient.core.record.ORecordSchemaAwareAbstract.validate(ORecordSchemaAwareAbstract.java:332)
    at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:1461)
    at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:1451)
    at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:1440)
    at document.Test_schimafull.main(Test_schimafull.java:92)

Schema-FullとSchema-lessをクラス(概念的にはテーブル)単位で指定可能というのは非常に面白いですね。

4.0トランザクション

トランザクションに関してはまた別のチュートリアルで触れる予定です。
実装方法としては下記のコードを参照ください。
注意点としては、現状、remote接続では正しく動かない場合がある。
また、Schema-less状態でのフィールドの変更はトランザクションの外で実施する必要があります。

    try{
      db.begin();
      ...
      // WRITE HERE YOUR TRANSACTION LOGIC
      ...
      db.commit();
    }catch( Exception e ){
      db.rollback();
    } finally{
      db.close();
    }

5.0 サンプル実装1

さて、これまでの情報のまとめとしてCSVのから取得した情報を元にデータを登録するという小さなサンプルアプリを登録します。
CSVの読み込みに関してはorangesignalの非常に素敵なライブラリorangesignalCSVを利用しています。

package sample;

import java.io.File;
import java.io.IOException;
import java.util.List;

import jp.sf.orangesignal.csv.Csv;
import jp.sf.orangesignal.csv.CsvConfig;
import jp.sf.orangesignal.csv.handlers.StringArrayListHandler;

import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;

public class DocumentSample{

    /**
     * @param args
     */
    public static void main(String[] args) {
            ODatabaseDocumentTx database = null;
        try {

            //0. 登録用データの読み込み
            //日本全国の郵便局の名前と住所が入ったCSVファイル
            //こちらOrientDBとは関係ありません、CSVをパスしたものが一行毎にStringの配列に入っているものとしています
            List<String[]> postOffices = Csv.load(new File("P30-13_UTF8.csv"),new CsvConfig(), new StringArrayListHandler());

            //1. データベースへの接続

            //リモートでデータベースへ接続します。
            database = new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest").open("root", "root");

            //2. Documentの登録
            //CSVからListに格納された郵便局の情報を登録する
            for (String[] postOffice : postOffices) {
                //クラスを定義
                ODocument doc = new ODocument("PostOffice");
                //name を追加
                doc.field("name", postOffice[0]);
                //address を追加
                doc.field("address", postOffice[1]);

                // ドキュメントを保存
                doc.save();
            }

            //3. 登録したpostoffcieのデータのnameフィールドに湯島と前方一致するものを取得
            //Prepared queryを使用
            OSQLSynchQuery<ODocument> query = new OSQLSynchQuery<ODocument>("SELECT from postoffice where name like ?");
            List<ODocument> result = database.command(query).execute("湯島%");

            //4. 取得結果の表示

            for (ODocument doc : result) {
                System.out.println("Rec ID : " + doc.getIdentity());
                System.out.println("name   : " + doc.field("name"));
                System.out.println("address: " + doc.field("address"));
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (database != null) {
                database.close();
            }
        }
    }
}

6.0 リレーションを使おう!

上記のサンプルではClassとしてPostOfficeとして登録しましたが、たとえばPostOfficeをPOI(ポイント情報)として扱いそのPOIの種別の一つとしてPostOfficeがあるといった形。
つまりPOIの種別マスタテーブルみたいなのが欲しいといった場合はどうしたらよいでしょうか?

一般的なRDBMSの概念ではPOIの種別マスタテーブルを用意してそこをJoinするという対応ですよね。

OrientDBでも同様の対応が可能です。

Document間でのリレーションを作成

//マスタテーブルに相当するPoiTypeマスタークラスを作成.
//郵便局マスタを登録
    ODocument postofficeClass = new ODocument("POI_Type");
     postofficeClass.field("type_id",0 );
     postofficeClass.field("name","郵便局" );
     postofficeClass.save();
//postofficeClassIDインスタンスからRIDを取得します
     ORID postofficeClassID = postofficeClass.getIdentity();

//そんな感じで幾つか登録します。
//POI(位置情報)を登録します。poi_typeフィールドにpostofficeClassID のRIDを設定。
    ODocument doc = new ODocument("POI");
          doc.field("name", "東京中央郵便局");
      doc.field("address", "東京都千代田区丸の内2-7-2");
     doc.field("poi_type", postofficeClassID);

7.0 サンプルプログラム2

それでは、上記のサンプルプログラムに対してPOIマスターテーブルを作成する形へ変更してみます。

package document;

import java.io.File;
import java.io.IOException;
import java.util.List;

import jp.sf.orangesignal.csv.Csv;
import jp.sf.orangesignal.csv.CsvConfig;
import jp.sf.orangesignal.csv.handlers.StringArrayListHandler;

import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;

public class RelationTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        
        ODatabaseDocumentTx database = null;
        try {
            // 日本全国の郵便局の名前、住所、緯度、経度、管理番号が入ったCSV
            // こちらOrientDBとは関係ありません、CSVをパスしたものが一行毎にStringの配列に入っているものとしています
            List<String[]> postOffices = Csv.load(new File("P30-13_UTF8.csv"), new CsvConfig(), new StringArrayListHandler());

            // リモートでデータベースへ接続します。
            database = new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest").open("root", "root");

            // CREATE A NEW DOCUMENT AND FILL IT

            // NOTE: Changes to the schema are not transactional, so executethem outside a transaction.
            // ちなみにトランザクションの中ではスキーマは変更出来ません

            //マスタテーブルに相当するPoiTypeクラスを追加

            ODocument postofficeClass = new ODocument("PoiType");
            postofficeClass.field("type_id", 0);
            postofficeClass.field("name", "PostOffice");
            postofficeClass.save();

            ORID postofficeClassID = postofficeClass.getIdentity();

            // さて

            for (String[] postOffice : postOffices) {
                ODocument doc = new ODocument("PostOffice");
                doc.field("name", postOffice[0]);
                doc.field("address", postOffice[1]);
                doc.field("poi_type", postofficeClassID);

                // SAVE THE DOCUMENT
                doc.save();

            }

            //Prepared queryを使用
            OSQLSynchQuery<ODocument> query = new OSQLSynchQuery<ODocument>("select from PostOffice where poi_type.name=?");
            List<ODocument> result = database.command(query).execute("PostOffice");

            for (ODocument doc : result) {
                // レコードIDを表示
                System.out.println("RID : " + doc.getIdentity());

                // POIクラスに含まれる全てのフィールド名とその値を表示
                for (String fieldName : doc.fieldNames()) {

                    System.out.println(fieldName + " : " + doc.field(fieldName) + " " + doc.fieldType(fieldName));
                }

                // poi_type.nameを取得
                System.out.println("poi_type.name" + " : " + doc.field("poi_type.name"));

            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (database != null) {
                database.close();
            }
        }
    }
}