MySQL 8.0 では、
- JSON 機能
- X DevAPI
を活用すればドキュメントデータベース(ドキュメントストア)としても使えるよ、ということなので、遅ればせながら Java(8)環境で MySQL Connector/J を使って試してみました。
【参考】
準備
以下を同一のマシン(Windows PC でも Linux 仮想マシンでも OK)にインストールします。
- MySQL Community Server 8.0.15
- Java SE 8 JDK / JRE(とりあえず OpenJDK ベースものなら大丈夫かと)
- MySQL Connector/J 8.0.15
- テストコード(後述)
※テストコードから MySQL Connector/J にパスを通した状態で .jar 化するなどしてください。面倒なのでわたしは Windows 10 に MySQL Community Server をインストールし、Maven で MySQL Connector/J を組み込んで Eclipse 上でコードを実行しました。
次に、MySQL(Community)Server 上にテスト用のユーザを登録しておきます(コマンドラインからでも MySQL Workbench からでも OK)。
- ユーザ名は
testuser@localhost
- パスワードは
T35_U53r
- SELECT, INSERT, UPDATE, DELETE 権限
- 認証は
caching_sha2_password
でもmysql_native_password
でも OK
最後に、CREATE DATABASE
などで DB(スキーマ)test_db
を作成して準備完了です。
テストコードの説明
↓が簡単なドキュメント処理をするテストコードです。
package site.hmatsu47.DocDbTest;
import java.util.List;
import com.mysql.cj.xdevapi.Collection;
import com.mysql.cj.xdevapi.DbDoc;
import com.mysql.cj.xdevapi.DocResult;
import com.mysql.cj.xdevapi.Schema;
import com.mysql.cj.xdevapi.Session;
import com.mysql.cj.xdevapi.SessionFactory;
public class Main {
public static void main(String args[]) {
// サーバに接続
Session session = new SessionFactory().getSession("mysqlx://localhost:33060/test_db?user=testuser&password=T35_U53r");
// DBに接続
Schema db = session.getSchema("test_db");
// コレクション'test_collection'を作成
Collection col = db.createCollection("test_collection", true);
// コレクションにドキュメントを追加
col.add("{\"id\":1, \"name\":\"さくらばさん\", \"Program\":\"Java Champion\"}")
.execute();
col.add("{\"id\":2, \"name\":\"よくさん\", \"Program\":\"Oracle ACE\"}")
.execute();
col.add("{\"id\":3, \"name\":\"せろさん\", \"Program\":[\"Java Champion\",\"Oracle Groundbreaker Ambassador\"]}")
.execute();
col.add("{\"id\":4, \"name\":\"とみたさん\", \"Program\":\"Oracle ACE Associate\"}")
.execute();
col.add("{\"id\":5, \"name\":\"みたにさん\", \"Program\":\"Oracle ACE\"}")
.execute();
// 本題とは関係ないのですが、OracleのACE・Groundbreaker・Java ChampionのDBにじゅくちょーさんが見当たらないのです…
// コレクションの「id」列にインデックスを追加
col.createIndex("id_index", "{\"fields\": [{\"field\": \"$.id\", \"type\": \"INT\"}]}");
// コレクションから「Program LIKE '%Oracle%'」を探して表示
searchProgram(col, "Oracle");
System.out.println();
// コレクションから「Program LIKE '%Java%'」を探して表示
searchProgram(col, "Java");
System.out.println();
// コレクションから「id=2」を探して表示
searchId(col, 2);
System.out.println();
// コレクションから「id=4」を探して表示
searchId(col, 4);
// コレクションを削除
db.dropCollection("test_collection");
}
// コレクションから対象ドキュメントの「Program」を文字列検索して表示する
private static void searchProgram(Collection col, String keyword) {
System.out.println("Search: " + keyword);
DocResult docs = col.find("Program like :program")
.bind("program", "%" + keyword + "%").execute();
// 結果を取得して表示
List<DbDoc> docl = docs.fetchAll();
docl.forEach(doc -> System.out.println(doc.toFormattedString()));
}
// コレクションから対象ドキュメントの「id」を数値検索して表示する
private static void searchId(Collection col, long value) {
System.out.println("Search: " + value);
DocResult docs = col.find("id = :id")
.bind("id", value).execute();
// 結果を取得して表示
System.out.println(docs.fetchOne().toFormattedString());
}
}
new SessionFactory().getSession()
で引数にURL
を指定して DB サーバに接続し、session.getSchema();
で引数にスキーマ(DB)名を指定して DB に接続しています。
なお、URL
はその名の通りURL
ですので、パスワードに記号を使っている場合は適宜エスケープ処理をしてください(わたしはうっかりミスをして小一時間悩みました)。
その後、順に
- ドキュメントコレクションの新規作成(同名のものが既に存在するときはそのまま使う)
- ドキュメントコレクションにドキュメントを追加(計 5 行)
- インデックス作成
- 文字列
LIKE
検索 - 数値検索(インデックス使用を意図したもの)
- ドキュメントコレクションの削除
を行っています。
見ての通り、文字列LIKE
検索では、1 つのドキュメントに同じ名前の要素を複数持つ場合でも正しく検索されていることがわかります。
実行途中にテーブル設計を見るとこうなります(SHOW CREATE TABLE test_db.test_collection;
)
CREATE TABLE `test_collection` (
`doc` json DEFAULT NULL,
`_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
`$ix_i_C6EF652A87142734264FBA60F41C59108CF4D8CA` int(11) GENERATED ALWAYS AS (json_extract(`doc`,_utf8mb4'$.id')) VIRTUAL,
PRIMARY KEY (`_id`),
KEY `id_index` (`$ix_i_C6EF652A87142734264FBA60F41C59108CF4D8CA`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
また、実行結果はこうなります。
Search: Oracle
{
"Program" : "Oracle ACE",
"_id" : "00005c8ded80000000000000006b",
"id" : 2,
"name" : "よくさん"
}
{
"Program" : ["Java Champion", "Oracle Groundbreaker Ambassador"],
"_id" : "00005c8ded80000000000000006c",
"id" : 3,
"name" : "せろさん"
}
{
"Program" : "Oracle ACE Associate",
"_id" : "00005c8ded80000000000000006d",
"id" : 4,
"name" : "とみたさん"
}
{
"Program" : "Oracle ACE",
"_id" : "00005c8ded80000000000000006e",
"id" : 5,
"name" : "みたにさん"
}
Search: Java
{
"Program" : "Java Champion",
"_id" : "00005c8ded80000000000000006a",
"id" : 1,
"name" : "さくらばさん"
}
{
"Program" : ["Java Champion", "Oracle Groundbreaker Ambassador"],
"_id" : "00005c8ded80000000000000006c",
"id" : 3,
"name" : "せろさん"
}
Search: 2
{
"Program" : "Oracle ACE",
"_id" : "00005c8ded80000000000000006b",
"id" : 2,
"name" : "よくさん"
}
Search: 4
{
"Program" : "Oracle ACE Associate",
"_id" : "00005c8ded80000000000000006d",
"id" : 4,
"name" : "とみたさん"
}
その他
X DevAPI には、ほかにも
- 非同期処理
- リレーショナルな DB スキーマに対する CRUD 等の操作
- トランザクション処理
など、いくつかの機能があります。