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 Type 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プロトコル
MySQLProtocolは、MySQLドライバーとMySQLServer間の通信を実現しており、同一のシリアライズおよびデシリアライズ規定により、データの送信側と受信側が正確かつ効率的にPRC呼び出しを行うことを可能にします。WireSharkソフトウェアはMySQLProtocolの形式でのデコードをサポートしているため、パケットキャプチャは比較的容易です。以下の図を参照してください:

これにより、ドライバーとOBServerノード(ODP)間のやり取りプロセスやパケット情報を比較的明確に確認することができます。
MySQLProtocolが存在するため、それをサポートし、動作がMySQLServerと一致すれば、MySQL JDBCおよびその他のMySQLProtocolベースのドライバー(例:MariaDB JDBC)との互換性を実現できます。したがって、OBServerノードはMySQL JDBCと互換性があり、またMySQL JDBCやMariaDB JDBCを基盤としてOceanBaseをサポートするOracle ModeのOBJDBCも開発されています。
テキストプロトコルとバイナリプロトコル
MySQL Protocolには、テキストプロトコルとバイナリプロトコルという重要な概念があります。簡単に言えば、以下のようにまとめられます:
テキストプロトコル:JDBCのすべてのアクションを標準的なSQL文に変換し、そのSQL文を文字列形式でOBServerノードに送信します。OBServerノードが実行後、対応する結果を返します。
バイナリプロトコル:プロトコル内のCOM_STMT_PREPAREコマンドを使用して対応するSQL文をPrepareし、OBServerノードがSQL文を解析して対応するパラメータの説明情報を返します。ドライバー側では、対応するデータ情報を埋め込んだ後、再びOBServerノードに送信します。
テキストプロトコルの利点は、一度Prepareを実行せず、対応する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はMariaDB JDBCに基づいて開発されており、オープンソースライセンスはLGPL、JDK要件は1.8です。
JDBC標準実装
接続関連
接続の確立
OceanBaseデータベースは、テナントによってOracleモードとMySQLモードを区別します。MySQLモードの場合、MySQL Serverと基本的に同じ動作をします。また、MySQL JDBCを使用してOBServerに直接接続することも可能ですが、ここではMySQLモードについては触れず、Oracleモードの場合のみを分析します。
MySQLでの接続確立プロセスはHandshakeと呼ばれ、その流れは以下の通りです(ここで説明するプロセスは、基盤となるTCP接続が確立された後のものです):
OBServer側からClient側へHandshakeパケットが送信されます。このパケットには、OBServer側のバージョン、文字セット、サポートする機能などの情報が含まれます。
Client側は、Handshakeパケットに記載されている情報に基づいてHandshake Responseパケットを構築し、OBServerノードに送信して認証を行います。
認証に成功すると、OBServerノードはClientにOKパケットを返し、これにより接続確立のやり取りが完了します。
OKパケットにはStatus Flagsという2バイトのフラグが含まれており、現在の接続状態を示すために使用されます。これは合計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パケットには、Status Flagsという2バイトのフラグが含まれており、現在の接続状態を示すために使用されます。これは合計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
PrepareStatementの機能
PrepareStatementはJDBCで最もよく使われるインターフェースの一つであり、Statementのほぼすべての機能を実現できます。PrepareStatementとは、プリコンパイルされた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オブジェクトは更新不可能であり、前方へのみ移動可能なカーソルを持っています。そのため、結果セットは最初の行から最後の行まで一度しかイテレーションできませんが、スクロール可能または更新可能なResultSetオブジェクトを生成することもできます。
ResultSetインターフェースは、現在の行から列値を取得するためのGetterメソッド(GetBoolean、GetLongなど)を提供します。列のインデックス番号または列名を使用して値を取得できます。一般的に、列インデックスを使用した方が効率的です。列は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チェックサムアルゴリズムの純粋なJava実装であり、CRC32-C多項式を使用します。iSCSIで使用される多項式は、SSE4.2をサポートする多くのIntelチップセットで実装されている多項式と同じです)を用いてCheckSumを計算し、そのCheckSumをすべてのExecuteに送信します。CheckSumにエラーが発生した場合、OBServerノードはエラーを報告します。
詳細情報
OceanBase Connector/Jの詳細と使用方法については、『OceanBase Connector/J』を参照してください。