OceanBaseデータベースは、OceanBase Connector/Jを通じてJavaで開発されたクライアントアプリケーションへの接続を提供します。Javaデータベース接続(Java Database Connectivity、JDBC)は、Javaからリレーショナルデータベースに接続するためのインターフェースであり、Javaの標準です。OceanBase Connector/Jは、JDBC APIを実装したドライバーです。
JDBC標準はSun Microsystemsによって定義され、標準のjava.SQLインターフェースを通じて実装されています。これにより、各プロバイダーが独自のJDBCドライバーを使用して標準を実装および拡張できます。JDBCは、X/Open SQLに基づく呼び出しレベルインターフェース(Call Level Interface、CLI)です。
主な機能
OceanBase Connector/Jの主要なモジュールの機能は以下のとおりです:
DriverManager:ドライバー(Driver)をロードし、呼び出しリクエストに応じて対応するデータベース接続(Connection)を返します。
Driver:ドライバーはDriverManagerにロードされ、リクエストを処理して対応するデータベース接続(Connection)を返す責任を負います。
Connection:データベース接続は、データベースとの通信を担当し、SQL実行およびトランザクション処理のための接続環境を提供し、Statementの作成と実行を行います。
Statement:
Statements:SQLクエリと更新を1回だけ実行するために使用されます。
PreparedStatement:キャッシュされたStatementを実行するために使用され、実行パスが事前に決定されています。繰り返し実行をサポートし、実行効率を向上させます。
CallableStatement:データベース内のストアドプロシージャを実行するために使用されます。
SQLException:データベースへの接続の作成・閉じる際、またはSQLステートメントの実行時に発生したエラーを示します。
OceanBase Connector/Jドライバー
OceanBase Connector/JドライバーはJDBCタイプ4ドライバーであり、ローカルプロトコルを介してデータベースエンジンと直接通信します。ODP/OBServerノードはOceanBase Connector/Jドライバーをサポートしており、MySQLのネイティブJDBCドライバー(MySQL Connector Java)と完全に互換性があります。OceanBase Connector/JドライバーはMySQL JDBCの使用方法と完全に互換性があり、OceanBaseデータベースの実行モードがMySQLモードかOracleモードかを自動的に認識し、プロトコルレベルで両方のモードと互換性があります。
OceanBase Connector/JドライバーはOB2.0プロトコルと互換性があります。
注意
OBServerノードは、JDBCドライバー接続時のテナント名に基づいて、実行モードをMySQLモードまたはOracleモードと判断します。Oracleモードのテナントでは、Oracle互換のSQL構文のみの使用が許可されます。
標準のJDBCアプリケーションプログラミングインターフェース(API)をサポートするだけでなく、OceanBase Connector/JはOracle Driverの使用方法とも互換性があります。OBServerノードのOracleモードは、Oracleのほとんどの構文と互換性があります。
MySQLプロトコル
MySQLプロトコルは、MySQLドライバーとMySQLサーバー間の通信を実現します。同じシリアライズおよびデシリアライズの規定により、データの送信側と受信側が正確かつ効率的にPRC呼び出しを行うことが可能です。WireSharkソフトウェアはMySQLプロトコル形式でのデコードをサポートしているため、パケットキャプチャは比較的容易です。以下の図のように:

ドライバーとOBServerノード(ODP)間のインタラクションプロセスやパケット情報を比較的明確に確認することができます。
MySQLプロトコルが存在するおかげで、それをサポートし、動作がMySQLサーバーと一致すれば、MySQL JDBCおよびその他MySQLプロトコルベースのドライバー(例:MaraiDB JDBC)との互換性を実現できます。したがって、OBServerノードはMySQL JDBCと互換性があり、MySQL JDBCおよびMariaDB JDBCを基盤にOceanBaseをサポートするOracleモードのOBJDBCが開発されています。
テキストプロトコルとバイナリプロトコル
MySQLプロトコルには、テキストプロトコルとバイナリプロトコルという重要な概念があります。簡単に言えば、次のように要約できます:
テキストプロトコル:JDBCのすべての操作を標準SQL文に変換し、そのSQL文を文字列としてOBServerノードに送信します。OBServerノードが実行した後、対応する結果を返します。
バイナリプロトコル:プロトコル内のCOM_STMT_PREPAREコマンドを使用して対応するSQL文をPrepareします。OBServerノードがSQL文を解析し、対応するパラメータの説明情報を返します。ドライバー側が対応するデータ情報を埋め込んだ後、再びOBServerノードに送信します。
テキストプロトコルの利点は、Prepareを1回実行する必要がなく、対応するSQLを直接実行できることです。バイナリプロトコルの利点は、Prepare後に複数回実行できることです。
これに密接に関連する構成パラメータはuseServerPrepStmtsです。この値がFalseの場合はテキストプロトコルを採用し、Trueの場合はバイナリプロトコルを採用します。
例:
PreparedStatement ps = conn.prepareStatement("select * from selecttest");
ResultSet rs = ps.executeQuery();
上記のテスト文は、useServerPrepStmts=trueとuseServerPrepStmts=falseの場合で動作が異なります:
Request Prepare Statement
Response
Request Execute Statement
Response
パケットキャプチャデータから、useServerPrepStmts=trueの場合はprepare + executeが行われ、useServerPrepStmts=falseの場合はSQL文が直接実行されていることが確認できます。
Request Query {select * from selecttest}
Response
OBJDBCのバージョン違い
1.XはMySQL JDBCを基に開発され、オープンソースライセンスはGPL、JDKバージョン要件はJDK7〜8です。
2.XはMaraiDB JDBCを基に開発され、オープンソースライセンスはLGPL、JDK要件は1.8です。
JDBC標準実装
Connection関連
接続の確立
OceanBaseデータベースは、テナントを通じてOracleモードとMySQLモードを区別します。MySQLモードの場合は、MySQL Serverと基本的に同様の動作をし、MySQL JDBCを使用してOBServerに直接接続することもできます。ここではMySQLモードの場合は議論せず、Oracleモードの場合のみ分析します。
MySQLでの接続確立のやり取りプロセスはHandshakeと呼ばれ、全体の流れは以下のとおりです(ここで説明するプロセスは、基盤となるTCP接続が確立された後のものです):
OBServer側からClient側へHandshake Packetが送信されます。このパケットには、OBServer側のバージョン、文字セット、サポートする機能などの情報が含まれます。
Client側は、Handshake Packetに含まれる情報に基づいてHandshake Response Packetを構築し、OBServerノードに送信して認証を行います。
認証が成功すると、OBServerノードはClientにOK Packetを返し、これにより接続確立のやり取りが完了します。
OKパケットには2バイトのStatus Flagsが含まれており、現在の接続状態情報を表します。これは合計16ビットで、各ビットが一つの機能を表します。

Handshake Responseパケットでは、最初の4バイトcapability flagsがClientがサポートする機能を記述します。長さは32ビットで、各ビットが一つの機能を表します。現在、MySQLは最初の26ビットを使用していますが、私たちは27番目のビットを使用してClientがOracleモードをサポートするかどうかを示します。具体的な内容は以下のとおりです:
const (
clientLongPassword clientFlag = 1 << iota
clientFoundRows
clientLongFlag
clientConnectWithDB
clientNoSchema
clientCompress
clientODBC
clientLocalFiles
clientIgnoreSpace
clientProtocol41
clientInteractive
clientSSL
clientIgnoreSIGPIPE
clientTransactions
clientReserved
clientSecureConn
clientMultiStatements
clientMultiResults
clientPSMultiResults
clientPluginAuth
clientConnectAttrs
clientPluginAuthLenEncClientData
clientCanHandleExpiredPasswords
clientSessionTrack
clientDeprecateEOF
clientSupportOracleMode = 1 << 27
)
OBServerノードがHandshake Responseパケットを受信すると、usernameに含まれるテナント情報を解析します。それがOracleテナントであり、かつcapability flagsの27番目のビットが1の場合、ClientにOKパケットを返します。そうでない場合は、エラーパケットを返し、エラーメッセージは「Oracle tenant for current client driver is not supported」となります。返されるOKパケットには、2バイトのStatus Flagsが含まれており、現在の接続状態情報を表します。これは合計16ビットで、そのうち3番目のビットはMySQLでは使用されていないため、このビットを直接再利用して現在の接続がOracleモードであることを示します。
Session変数の初期化
OBJDBCがOBServerノード/ODPとの接続に成功すると、一連の変数初期化が行われます。初期化される変数の値はデフォルト設定から取得され、URL内の設定で上書きすることができます。
通常、初期化時にはSQLを実行して変数を初期化します。例:
set autocommit=1, sql_mode = concat(@@sql_mode,',STRICT_TRANS_TABLES')
set names utf8
ローカル変数の初期化
OBJDBCは、ReadOnlyやtxIsolationなどの値を保持することで、OBServerノードのSessionとの一貫性を保証します。Session変数の設定後、対応する変数値を照会してこれらのLocal Variablesを初期化し、その後APIを呼び出す際にも対応する値を修正することで、OBServer側とJDBC側の情報が一致するようにします。
例:
SELECT @@max_allowed_packet,@@system_time_zone,@@time_zone,@@auto_increment_increment,@@tx_isolation AS tx_isolation,@@session.tx_read_only AS tx_read_only from dual
select count(*) from V$TIMEZONE_NAMES // このステートメントは特別で、サーバーがタイムゾーンテーブルをインポートしているかどうかを確認するために使用されます。
Connectionのよく使われるインターフェース
Connectionは最も基本的なインターフェースであり、他のインターフェースを作成したり、Connection関連の情報を取得したりします。最もよく使われるインターフェースは以下のとおりです:
createStatement
prepareStatement
prepareCall
getMetaData
commit/rollback
Statementの機能
Statementは機能が比較的単一なインターフェースで、主にSQL文の実行に使用されます。データのクエリにも使用でき、OBClientなどのコマンドラインツールでSQLを実行することと本質的な違いはありません。
Statementのよく使われるインターフェースは以下のとおりです:
execute
executeQuery
setQueryTimeout
addBatch
executeBatch
setFetchSize
PreparedStatementの機能
PreparedStatementはJDBCで最もよく使われるインターフェースの一つで、Statementのほぼすべての機能を実現できます。PreparedStatementはプリコンパイルされたSQL文を表すオブジェクトで、SQL文はプリコンパイルされてオブジェクトに格納されます。カプセル化されたSQL文は特定の操作を表し、SQL文には動的パラメータ「?」が含まれることが許されています。実行時には「?」に動的にパラメータ値を設定できます。
注意点として、真のプリコンパイル効果を実現するには、url optionのuseServerPrepStmts = trueを設定する必要があります。そうでない場合、PrepareStatementを使用してもバイナリプロトコルではなくテキストプロトコルにダウングレードされます。
PrepareStatementの一般的な使い方は以下のとおりです:
ps = conn.prepareStatement("insert into pstest values(?,?)");
ps.setString(1,"string1");
ps.setInt(2,1);
ps.execute();
ps.setString(1,"string2");
ps.setInt(2,2);
ps.execute();
プリコンパイル後のデータはExecuteを送信するだけで済むため、効率が大幅に向上します。
PrepareStatementのよく使われるインターフェースは以下のとおりです:
addBatch
executeBatch
setFetchSize
CallableStatementの機能
CallableStatementはProcedureやFunctionを実行し、対応するIN、OUTパラメータを設定したり、戻り値を取得したりするために使用できます。本質的にCallableStatementはPrepareStatementのサブクラスであり、PrepareStatementとしても使用できます。
ConnectionのメソッドPrepareCallを実行する際、対応するSQL文が解析されます。もしそれがProcedureまたはFunctionを呼び出すSQL文であれば、対応する解析ツールを使用してProcedure/Functionの名前を解析し、その名前からall_argumentで対応するIN、OUT型を検索します。これらの情報を元に、対応するProcedure/Functionの説明情報を組み立てます。
CallableStatementで最もよく使われるメソッドは以下のとおりです:
registerOutParameter
getXXX
ResultSet
通常、データベースに対するクエリステートメントを実行することで、データベースの結果セットを表すデータテーブルが生成されます。
ResultSetオブジェクトは、現在のデータ行を指すカーソルを保持します。カーソルは最初、最初の行の前に位置しています。nextメソッドを使用すると次の行に移動できます。ResultSetオブジェクトに他の行がない場合はFalseを返すため、Whileループ内で使用して結果セットを走査できます。
デフォルトのResultSetオブジェクトは更新不可であり、前方へのみ移動可能なカーソルを1つ持っています。そのため、結果セットは最初の行から最後の行まで1回だけ走査できますが、スクロール可能または更新可能なResultSetオブジェクトを生成することもできます。
ResultSetインターフェースは、現在の行から列値を取得するためのGetterメソッド(GetBoolean、GetLongなど)を提供します。列のインデックス番号または列名を使用して値を取得できます。一般的に、列インデックスの方が効率的です。列は1から番号が付けられます。最大限の移植性を得るためには、各行の結果セット列を左から右の順に読み取り、各列は1回だけ読み取る必要があります。
Getterメソッドにおいて、JDBCドライバーは、基盤となるデータをGetterメソッドで指定されたJAVA型に変換し、適切なJAVA値を返すよう試みます。
Getterメソッドで入力として使用される列名は大文字小文字を区別しません。列名を使用してGetterメソッドを呼び出した際、複数の列が同名を持つ場合、最初にマッチする列の値が返されます。列名は、結果セットを生成するSQLクエリで使用されます。クエリで明示的に名前が付けられていない列については、列番号を使用するのが望ましいです。列名を使用する場合、プログラマーはSQLのAS句を使用して、それらが一意の列に参照されるようにすることができます。
関連するメソッドは以下の通りです:
next absolute previous
getMetaData
ResultSetMetaData機能
ResultSetMetaDataは、ResultSetオブジェクト内の列の型や属性に関する情報を取得するために使用でき、多くのインターフェースが提供するよりもはるかに詳細な説明情報を得ることができます。
Oracle互換性
Oracle互換性には主に2つの側面があります:データ型の互換性と機能の互換性です。前述の標準実装もOracle互換性に対応しています。例えば、Oracleの特定のデータ型との互換性を持たせるために、ResultSetMetaDataのメソッドの戻り値を調整する必要があります。CheckSumの実装では、既存のMySQLプロトコルの基盤の上にCheckSum機能を追加し、HandShakeパケットのStatus FlagをOracle MODE Flagとして利用します。
データ型の互換性
OceanBaseが互換するOracleデータ型は以下のとおりです:
文字列型
VARCHAR2
NVARCHAR2
CHAR
NCHAR
数字型
NUMBER
NUMBER_FLOAT
BINARY_FLOAT
BINARY_DOUBLE
日付型
DATE
TIMESTAMP
TIMESTAMP WITH TIME ZONE
TIMESTAMP WITH LOCAL TIME ZONE
INTERVAL
INTERVAL YEAR TO MONTH
INTERVAL DAY TO SECOND
LOB
CLOB
BLOB
複合型
Struct
Array
RefCursor
RAW
ROWID
機能の互換性
PS Checksum
Oracleモードでは、PS後のCheckSum検証を有効にできます。UseServerPsStmtChecksumパラメータがこの機能を有効または無効にする役割を果たします。
Preapre時にOBCrc32C(CRC32 checksumアルゴリズムの純粋なJava実装で、CRC32-C多項式を使用します。iSCSIで使用されている多項式は、SSE4.2をサポートする多くのIntelチップセットで実装されているものと同じです)を用いてCheckSumを計算し、送信されるすべてのExecuteに含めます。CheckSumに誤りがある場合、OBServerノードはエラーを報告します。
詳細を見る
OceanBase Connector/Jの詳細および使用方法については、『OceanBase Connector/J』を参照してください。