xDevAPITHEコーディング編

xDevAPI

やっとコーディング編へ…この章がないと若干意味がない気がしますが…急いで書かねばならないですね…

コーディングの感じ

コーディングしてみた感じは、やりやすいと思います。公式ドキュメントにも言語毎に例が記載されており、真似すればできてしまう感じです。
自分の感じでは非常に使いやすい感じがします。私の方であまり経験がない、ドキュメントストアというのはちょっと理解が深まらず、今回の関連記事では対象としませんが、それ以外は簡単にコーディングできるかな・・・という感覚です。

概要的な…

まずは、公式ドキュメントから
X DevAPI User Guideを見ると良いですね。
コーディングの流れ的には、
1.DBに接続する。(Sessionを作る)
2.SQLを実行する。
3.結果を取得する。
といった感じです…(;´Д`)あれ?あんまり書くことがないや。。
今回使うコード全体を先に以下に書いちゃいます。全部で20行以内ですね。
エラー処理を書いてないので、その辺は先にご了承願います。(/ω\)
コード説明は次章でやってきます。
以下がmain.cppです!

 1 #include <iostream>
 2 #include <list>
 3 #include <mysqlx/xdevapi.h>
 4
 5 using namespace ::mysqlx;
 6
 7 int main(int argc, const char* argv[])
 8 {
 9     std::string url = "mysqlx://root:password@172.17.0.2:33060";
10     mysqlx::Session mySession(url);
11     mySession.sql("USE test_db").execute();
12     mysqlx::SqlResult myResult = mySession.sql("SELECT * FROM mytest;").execute();
13     std::cout << "row count:"<< myResult.count() << std::endl;
14     std::list<mysqlx::Row> rows = myResult.fetchAll();
15     for(mysqlx::Row row:rows)
16     {
17         std::cout << row[0] << ":" << row[1] << std::endl;
18     }
19 }

コード説明

DBへの接続

DBへの接続には「Session」を作ります。Sessionは単発の接続になります。
Sessionを維持して再利用するという方法があります。これはSessionプールって呼ばれてます。
接続する手順を毎回やると時間かかるので、接続済みのSessionを複数プールしておいて、再利用しましょうっていう方法です。これは、性能を上げたいときなんかに使います。
まずは、Sessionに焦点を当てて説明します。
公式ドキュメントは、以下の部分を参照です。
Chapter 2 Connection and Session Concepts
でコーディングの例は以下で説明してます。
2.1 Database Connection Example
今回使いソースでは以下の部分ですね。

 9     std::string url = "mysqlx://root:password@172.17.0.2:33060";
10     mysqlx::Session mySession(url);

2行で書きましたけど1行で書いてもOKです。
“mysqlx://root:password@172.17.0.2:33060”;の部分は公式ドキュメントではURIのような物という説明をされています。
形式については、以下で詳しく説明されています。
2.2.5 Connection Option Summary
そこを見ていただければよいかと思うけど、張り付けておきます。

この2行でDBへの接続は終わりです。
Session作成については、URI方式とOption指定の方式があります。
xdevapi.hを見ると、exampleが書いてあるので、その辺も張り付けておきます。

    Examples:
    ~~~~~~
      Session from_uri("mysqlx://user:pwd@host:port/db?ssl-mode=disabled");

      Session from_options("host", port, "user", "pwd", "db");

      Session from_option_list(
        SessionOption::USER, "user",
        SessionOption::PWD,  "pwd",
        SessionOption::HOST, "host",
        SessionOption::PORT, port,
        SessionOption::DB,   "db",
        SessionOption::SSL_MODE, SSLMode::DISABLED
      );
    ~~~~~~

ちょっと脱線して複数DBの接続

xDevAPIのドキュメントを調べてて一番目を引かれたのがこの部分ですね。
2.2.2 Connecting to One of Multiple Hosts and Connection Failover

You can provide multiple MySQL Router or server endpoints (as host and an optional port, or Unix sockets) when creating a session. You specify a URI-like string which contains multiple hosts, ports, and optional priority. This enables the connector to perform automatic connection failover selection when any of the endpoints are not available.

セッションの作成時に、複数の MySQL Router またはサーバー エンドポイント (ホストおよびオプションのポート、または Unix ソケットとして) を指定できます。 複数のホスト、ポート、およびオプションの優先順位を含む URI のような文字列を指定します。 これにより、いずれかのエンドポイントが利用できない場合に、コネクタが自動接続フェイルオーバー選択を実行できるようになります。

つまりは接続先を複数指定できますよ!ってことです。さらに、接続できないエンドポイントがあれば自動的に切り替えるぜ!ってことです。

When multiple endpoints are available, the choice of which server is used for the session depends on whether you specify priority. If a priority is specified for each endpoint, the available endpoint with the highest priority is used. If you do not specify priority, a random available endpoint is used.

複数のエンドポイントが使用可能な場合、セッションにどのサーバーを使用するかの選択は、優先順位を指定するかどうかによって異なります。 各エンドポイントに優先順位が指定されている場合は、最も高い優先順位を持つ利用可能なエンドポイントが使用されます。 優先順位を指定しない場合は、ランダムな使用可能なエンドポイントが使用されます。

優先順位なんかも付けられるって話ですね。良いですね。
どんな時に使うのか?って話ですけど、最近はAWSなんか使ってシステム構築することが多くなっていると思います。
AWSだと以下のような感じでDBを使うことが一般的かな?と思います。

この構成だと通常クライアントサーバーは、クラスターエンドポイントでアクセスするのでwriter側にしかアクセスしないため、readerのDBは、障害などが発生して切り替わるまでは遊んでいるように見えちゃうわけです。遊んでるわけではないけどね・・・(‘ω’)
そこで、参照系のSQLなんかはreaderのエンドポイントへ流せば、性能上がるじゃん!って思うですけど、それだと、readerのDBが障害になったときに接続できなくなるよね?ってなるので、以下のようにreaderを複数持つような構成にするのがベストなんじゃない?って思うわけです。

ただ、ここで問題は・・・AWSのデータベースサービスは高い!ってことですよ!
readerを複数持つなんてしたくない!だって高いんだもん!って思ったときにこのxDevAPIの切り替え機能は使える!って感じましたね。イメージは以下のようになります。

ただ、これ、プール機能でも使えるの?ってところは問題…公式ドキュメントを見ると書いてないし、これ見よがしに
2.2.3 Connecting to a Single MySQL Server Using Connection Pooling
って、書いてあるので、マルチサーバーに対応できてなんかな?って思っています。
この部分はあとで試してみたいですね!
ちょっと長かったですけど・・・脱線はここまです。
・・・長いよ・・・(`・ω・´)

DBスキーマ選択

11行目、特別なことはなく、mysqlサーバーへログインしたときに使うUSEコマンドを打って、DBスキーマを切り替えているだけです。
ここ毎回やらんといかんのかね?(/・ω・)/
と思うところですが、そんなこともないですね。先に書いたexampleでdbってパラメータがあるので、ディフォルトで使用するDBスキーマを指定できると思います。・・・どこかで試しますけど・・・まぁ今回は省略ってことで!

11     mySession.sql("USE test_db").execute();

SQLを実行

12行目SQL実行。SELECT文を実行してます。後ほど説明しますけど、テスト用にmytestってテーブルを追加してます。

12     mysqlx::SqlResult myResult = mySession.sql("SELECT * FROM mytest;").execute();

結果取得

結果を取得して、リプライ表示している部分です。
13行目:std::cout << “row count:”<< myResult.count() << std::endl;は取得したレコード数を出力。
14行目は:std::list<mysqlx::Row> rows = myResult.fetchAll();は、レコードを取得して、listに入れてます。(見たマンマですけど。。。(・´з`・)
15~18行目はそれを出力ですね。

13     std::cout << "row count:"<< myResult.count() << std::endl;
14     std::list<mysqlx::Row> rows = myResult.fetchAll();
15     for(mysqlx::Row row:rows)
16     {
17         std::cout << row[0] << ":" << row[1] << std::endl;
18     }


これで、コードの説明はおしまいです。でも、このまま、動くかっていうとそうでもないですね。DBがないですね。また・・・ダメじゃんっ(/ω\)って今気が付きました。

DB準備

xDevAPI動かしてみよう編」で説明したので細かい話は割愛します。
xDevAPI動かしてみよう編」との違いは、「パスワードあり」、ポートは「33060」(ディフォルト)を使います。
あと接続するためにテーブルも必要じゃん!ってことでテーブルを作るまでを説明します。
xDevAPI動かしてみよう編」でみなさんDocker大好きになったんじゃないかなって思うし・・・
あとDockerコンテナをつかったのは、まぁこのxDevAPIで使える機能で複数のデータベースに同時にSessionを張ることができるっていう機能を試したい!っていう狙いもあったりしますが
…まだ、それは先の話!(・´з`・)
さて、余計な話はおいておいて、Dockerfileは以下でーす。

FROM mysql:8.0.35

ENV MYSQL_ROOT_PASSWORD password
ENV MYSQL_DATABASE test_db
ENV TZ Asia/Tokyo

EXPOSE 3306 33060

CMD ["mysqld"]

MYSQL_ROOT_PASSWORDは、rootのパスワード「password」に設定です。
ディフォルトのスキーマは「test_db」です。
Dockerコンテナが動作するHOSTマシンとのポートは「3306」と「33060」を開きます。
あとは、Dockerイメージを作って、Dockerコンテナを起動のために以下のコマンドを実行します。

★Dockerイメージを作る。
sudo docker build ./ -t testmysql:0.0
★Dockerコンテナを起動
sudo docker run -it -d --privileged --rm --name testmysql testmysql:0.0

Dockerコンテナが動いたかは以下で確認です。

# sudo docker ps
CONTAINER ID   IMAGE           COMMAND                   CREATED          STATUS          PORTS                 NAMES
5dbc8e03d628   testmysql:0.0   "docker-entrypoint.s…"   43 seconds ago   Up 21 seconds   3306/tcp, 33060/tcp   testmysql

これでDBの方はOK。
次に接続先のテーブルも作りましょう。
コンテナにログインして、mysqlコマンドでテーブル作成という手順です。
コンテナログイン

sudo docker exec -it testmysql /bin/bash
bash-4.4#←こいつが出る。

mysqlのログインはちょっと前回とは違います。

bash-4.4# mysql -u root -p
Enter password:←"password"と入力してEnter
↓成功すると以下が表示される。
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.35 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

でログインできたらまず使用DBスキーマを選択。

mysql> use test_db
以下リプライがでます。
Database changed

テーブルを作成以下のコマンドです。
知っている人は好きに作っていただいて問題ないかと…

mysql> create table mytest (id int, name varchar(10));
Query OK, 0 rows affected (0.76 sec)

テーブルで来たか確認したいという人は以下を投入…

mysql> show tables;
+-------------------+
| Tables_in_test_db |
+-------------------+
| mytest            |
+-------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.76 sec)

で、レコードがないと、SELECT文とか試せないのでいくつかレコードを追加・・・

mysql> INSERT INTO mytest (id,name) VALUES (1,'taro'),(2,'hanako');
Query OK, 2 rows affected (0.07 sec)
Records: 2  Duplicates: 0  Warnings: 0

内容の確認は以下で・・・

mysql> SELECT * FROM mytest;
+------+--------+
| id   | name   |
+------+--------+
|    1 | taro   |
|    2 | hanako |
+------+--------+
2 rows in set (0.00 sec)

これで準備完了です。

さあ、動かすぞ・・・と思たけれど、ビルドが・・・

ソースはできたが・・・ビルドどうすんでしょ?(‘ω’)
確かに~・・・
って思いました。でどうするかというとCmake使おうかなと思いましたね。
これまた、Cmakeの説明しているといつまでたっても動かせへんので・・・ここはCMkaeListと張り付けて、説明する程度で許してもらえるかなと。。。(;´Д`)
CMkaeList.txtは以下です。

 1 make_minimum_required(VERSION 3.13)
 2 set(CMAKE_CXX_STANDARD 17)
 3 set(CMAKE_C_COMPILER "/opt/rh/devtoolset-11/root/usr/bin/gcc")
 4 set(CMAKE_CXX_COMPILER "/opt/rh/devtoolset-11/root/usr/bin/g++")
 5 project(test_cmake CXX)
 6 set(MYSQL_CONCPP_DIR "/usr/local/mysql/connector-c++-8.2.0")
 7 set(MYSQL_CONNECTOR_CPP_LIBS_DIR "/usr/local/mysql/connector-c++-8.2.0/lib64/debug")
 8 set(MYSQL_CONNECTOR_INCLUDE_DIR "/usr/local/mysql/connector-c++-8.2.0/include")
 9 find_library(
10         MYSQL_CONN_CPP_LIBS
11         NAMES libmysqlcppconn8-static.a
12         HINTS "${MYSQL_CONNECTOR_CPP_LIBS_DIR}"
13         NO_DEFAULT_PATH
14 )
15 add_executable(test_conncpp main.cpp)
16 target_compile_options(test_conncpp PUBLIC -O2 -Wall -g)
17 target_compile_features(test_conncpp PUBLIC cxx_std_17)
18 target_compile_definitions(test_conncpp PUBLIC -DSTATIC_CONCPP)
19 target_include_directories(test_conncpp PUBLIC ${MYSQL_CONNECTOR_INCLUDE_DIR})
20 target_link_libraries(test_conncpp ssl crypto pthread resolv ${MYSQL_CONN_CPP_LIBS})

各自環境に合わせて適宜変更してほしいです。一応ネットで最近のトレンドを追いつつ作りました。
説明するって言ったけど、張り付けてみたら案外わかるんじゃないの?って思っちゃったので説明は割愛します!(‘◇’)ゞ

改めて!ビルド

まずディレクトリの構成です。シンプルで良いですね!

任意のディレクトリ
├ main.cpp
└ CMakeList.txt

まずは、ビルド実施前に以下のコマンドを実行

# cmake3 -S . -B build

これを実施すると上記ディレクトリに「build」ってディレクトリが追加されます。

任意のディレクトリ
├ main.cpp
├ CMakeList.txt
└ build★追加されるよ!

で、ビルド

# cmake3 --build build

これでビルドが完了するとbuildディレクトリしたに「test_conncpp」ができます。

-rw-r--r-- 1 root root    12474  4月 21 21:32 CMakeCache.txt
-rw-r--r-- 1 root root     5207  4月 21 21:32 Makefile
-rw-r--r-- 1 root root     1504  4月 21 21:32 cmake_install.cmake
-rwxr-xr-x 1 root root 31180904  4月 21 21:38 test_conncpp★
drwxr-xr-x 5 root root     4096  4月 21 21:38 CMakeFiles

これでビルドまでおしまいです!

さぁ実行

さぁ実行です。特別なことはなく、buildディレクトリに移動して以下で実行です。

# cd build/
# ./test_conncpp
row count:2
1:taro
2:hanako

こんな感じで出力されたらOKです!

ああ終わったけど・・・C++のライブラリの行けてないところ

ソースコードで、

     17         std::cout << row[0] << ":" << row[1] << std::endl;

この部分、テーブルの項目名でできないの?って思いますよね?以下のイメージ・・・

     17         std::cout << row["id"] << ":" << row["name"] << std::endl;

で、これはC++ではできないそうです・・・悲しい(;´Д`)なぜ・・・?
でもそんなことになったら、移行対象のソースを全部1とか2とか書き換えないとダメじゃん!ってなるじゃぁないですか!
そんなのダメ絶対ダメ(´_ゝ`)
ということになり、ラッパークラスを検討しました!
ということで次回は「xDevAPIラッパー編」です。

コメント

タイトルとURLをコピーしました