Storage API 操作ガイド

はじめに

このドキュメントではユーザー情報を管理する簡単なサンプルプログラムを用いてApollo Storage APIの基本的な使い方を紹介します.

準備

サンプルプログラムで使用するキースペースとテーブルをCassandra上に作成します.以下の例ではtest_keyspaceキースペースとtest_user_tableテーブルを作成しています.ここでは3ノード以上からなるクラスタを想定して,データの複製を3ノードに分散配置するように設定しています(’replication_factor’: ‘3’).
cat << EOF > create_test_schema.cql
CREATE KEYSPACE test_keyspace WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '3'};
CREATE TABLE test_keyspace.test_user_table (id int PRIMARY KEY, name text, age int, email text);
EOF

# cqlshコマンドの実行
cqlsh -f create_test_schema.cql

Storage APIを利用したデータ操作の例

Storageインスタンスの生成

最初にStorageインスタンスを生成します.Storageインスタンスはキースペースのテーブル毎に作成する必要があります.つまり,複数のテーブルを使用する場合はテーブル毎にインスタンスを生成する必要があります.
public Storage createStorage(String keyspace, String table) {
    // ファクトリクラスを使用してインスタンスを生成します
    return StorageFactory.create(keyspace, table);
}

レコードに対するCRUD(Create/Read/Update/Delete)操作

Apollo Storageにおいては,テーブルは主キーにより一意に識別されるレコードの集合として構成され,さらに,レコードは複数のカラムの集合として構成されています.Storage APIでは,レコードはRowクラス,レコードの各カラム(主キーを含む)はColumnクラスとして表されます. Storage APIではレコードに対する以下のCRUD操作のAPIを提供します.
  • レコードの挿入: Create
  • レコードの取得: Read
  • レコードの更新: Update
  • レコードの削除: Delete
以降では各操作の使用方法を説明します.

レコードの挿入

レコードを挿入するには,対象レコードのRowインスタンスを引数として,Storage#put()メソッドを呼び出します.このとき,Rowインスタンスは主キーのColumnを含んでいる必要があります.
public boolean register(int id, String name, int age, String email) {
    Row row = new Row();
    // キー(この例では"id")は必須
    row.put(new Column("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT));
    row.put(new Column("name", ByteBuffer.wrap(name.getBytes()), DataType.STRING));
    row.put(new Column("age", (ByteBuffer) ByteBuffer.allocate(4).putInt(age).flip(), DataType.INT));
    row.put(new Column("email", ByteBuffer.wrap(email.getBytes()), DataType.STRING));
    return storage.put(row);
}

レコードの取得

レコードを取得するには,以下のいずれかのレコード取得条件を引数として,Storage#get()を呼び出します.
  • rowキー
  • rowキーおよびclusteringキー
  • secondary index
取得条件はPredicateインスタンスを用いて表します. このサンプルプログラムではrowキーであるidを取得条件に指定しています.
public Row get(int id) {
    Predicate predicate = new Predicate("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT, CompareOperator.EQ);
    ResultSet results = storage.get(Arrays.asList(predicate));
    return results.one();
}
取得結果はResultSetのインスタンスとして返ってきます.結果を用いて何らかの処理を行う場合はResultSet#one()もしくはResultSet#iterator()を使用してRowインスタンスを取り出します.Storage#get()はレコードの取得結果としてResultSetのインスタンスを返します.ResultSetRowのリストです.ResultSetIteratorインターフェースを実装しているため,一般的には,ループを用いて各Rowインスタンスを取得します.なお,事前にレコード結果件数が1件だと分かっている場合は,ResultSet#one()を用いることも出来ます.
// イテレータの使用例
ResultSet results = storage.get(predicate);
Iterator<Row> rows = results.iterator();
while(rows.hasNext()) {
    Row row = rows.next();
    // do something ...
}

レコードの更新

レコードを更新するには,更新条件(Predicate)と更新レコード(Row)を引数としてStorage#update()を呼び出します.
public boolean update(int id, String name, int age, String email) {
    Predicate condition = new Predicate("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT, CompareOperator.EQ);

    Row assignment = new Row();
    assignment.put(new Column("name", ByteBuffer.wrap(name.getBytes()), DataType.STRING));
    assignment.put(new Column("age", (ByteBuffer) ByteBuffer.allocate(4).putInt(age).flip(), DataType.INT));
    assignment.put(new Column("email", ByteBuffer.wrap(email.getBytes()), DataType.STRING));

    return storage.update(Arrays.asList(condition), assignment);
}
このサンプルプログラムでは全てのカラムを指定して更新していますが,Apollo Storageでは特定のカラムのみを更新することも可能です.例えばageのみを更新したい場合は,以下のようにageカラムのみのRowインスタンスを引数に渡します.
// キーの指定
Predicate condition = new Predicate("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT, CompareOperator.EQ);
Row assignment = new Row();
assignment.put(new Column("age", (ByteBuffer) ByteBuffer.allocate(4).putInt(age).flip(), DataType.INT));
storage.update(Arrays.asList(condition), assignment);

レコードの削除

レコードを削除するには,削除対象レコードのキー(Predicate)を引数としてStorage#delete()を呼び出します.
public boolean delete(int id) {
    Predicate predicate = new Predicate("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT, CompareOperator.EQ);
    return storage.delete(Arrays.asList(predicate));
}

サンプルプログラム

以下にサンプルプログラムの全体と,実行例を示します.

サンプルプログラムの全体

/**
 * Usage:
 *
 * このサンプル全体をStorageSample.javaというファイル名で保存し,以下のコマンドを実行して下さい.
 *
 * javac -cp /path/to/dependencies StorageSample.java
 * java -cp .:/path/to/dependencies StorageSample
 */
import com.orb.apollo.storage.api.Column;
import com.orb.apollo.storage.api.CompareOperator;
import com.orb.apollo.storage.api.DataType;
import com.orb.apollo.storage.api.Predicate;
import com.orb.apollo.storage.api.ResultSet;
import com.orb.apollo.storage.api.Row;
import com.orb.apollo.storage.api.Storage;
import com.orb.apollo.storage.api.StorageFactory;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Iterator;

class UserRepository {
    private final String KEYSPACE = "test_keyspace";
    private final String TABLE = "test_user_table";
    private final Storage storage;

    public UserRepository() {
        storage = createStorage(KEYSPACE, TABLE);
    }

    public Storage createStorage(String keyspace, String table) {
        return StorageFactory.create(keyspace, table);
    }

    public boolean register(int id, String name, int age, String email) {
        Row row = new Row();
        // キーは必須
        row.put(new Column("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT));
        row.put(new Column("name", ByteBuffer.wrap(name.getBytes()), DataType.STRING));
        row.put(new Column("age", (ByteBuffer) ByteBuffer.allocate(4).putInt(age).flip(), DataType.INT));
        row.put(new Column("email", ByteBuffer.wrap(email.getBytes()), DataType.STRING));
        return storage.put(row);
    }

    public Row get(int id) {
        Predicate predicate = new Predicate("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT, CompareOperator.EQ);
        ResultSet results = storage.get(Arrays.asList(predicate));
        return results.one();
    }

    public boolean update(int id, String name, int age, String email) {
        Predicate condition = new Predicate("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT, CompareOperator.EQ);

        Row assignment = new Row();
        assignment.put(new Column("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT));
        assignment.put(new Column("name", ByteBuffer.wrap(name.getBytes()), DataType.STRING));
        assignment.put(new Column("age", (ByteBuffer) ByteBuffer.allocate(4).putInt(age).flip(), DataType.INT));
        assignment.put(new Column("email", ByteBuffer.wrap(email.getBytes()), DataType.STRING));

        return storage.update(Arrays.asList(condition), assignment);
    }

    public boolean delete(int id) {
        Predicate predicate = new Predicate("id", (ByteBuffer) ByteBuffer.allocate(4).putInt(id).flip(), DataType.INT, CompareOperator.EQ);
        return storage.delete(Arrays.asList(predicate));
    }
}

public class StorageSample {
    public static void main(String... args) {
        UserRepository userRepo = new UserRepository();

        int id = 1;
        String name = "user1";
        int age = 25;
        String email = "user1@example.com";

        // Create
        userRepo.register(id, name, age, email);

        // Read
        System.out.println("=== after inserted ===");
        System.out.println(rowInfo(userRepo.get(1)));

        // Update
        age = 30;
        email = "user1@updated.com";
        boolean result = userRepo.update(id, name, age, email);
        if (result) System.out.println("Update was succeeded");
        else System.out.println("Update is failed");

        // Read
        System.out.println("=== after updated ===");
        System.out.println(rowInfo(userRepo.get(id)));

        // Delete
        result = userRepo.delete(id);
        if (result) System.out.println("Delete was succeeded");
        else System.out.println("Delete is failed");

        System.exit(0);
    }

    public static String rowInfo(Row row) {
        return String.format("id:%d, name:%s, age:%d, email:%s\n",
                row.get("id").getBytes().getInt(),
                new String(row.get("name").getBytes().array()),
                row.get("age").getBytes().getInt(),
                new String(row.get("name").getBytes().array()));
    }
}

サンプルプログラムの実行例

サンプルプログラムの実行例は以下のようになります. ここでは$APOLLO_ROOT/lib というディレクトリに必要な依存パッケージのJarファイルが置かれていると想定しています.

# コンパイル $ javac -cp "$APOLLO_ROOT/lib/*" StorageSample.java # 実行 $ java -cp "./:$APOLLO_ROOT/lib/*" StorageSample 2017-02-28 00:54:20,597 [INFO com.datastax.driver.core.ClockFactory] Using native clock to generate timestamps. 2017-02-28 00:54:20,681 [INFO com.datastax.driver.core.NettyUtil] Did not find Netty's native epoll transport in the classpath, defaulting to NIO. ... === after inserted === id:1, name:user1, age:25, email:user1 Update was succeeded === after updated === id:1, name:user1, age:30, email:user1 Delete was succeeded
Share on FacebookTweet about this on TwitterShare on LinkedInEmail to someone