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 Protocol
MySQLProtocolは、MySQLドライバーとMySQLServer間の通信を実現します。同じシリアライズとデシリアライズの規定により、データの送信側と受信側が正確かつ効率的にPRC呼び出しを行えるようにします。WireSharkソフトウェアはMySQLProtocolの形式でのデコードをサポートしているため、パケットキャプチャは比較的容易です。以下の図のように:
ドライバーとOBServerノード(ODP)間のやり取りプロセスとパケット情報を比較的明確に確認できます。
MySQLProtocolが存在するため、それをサポートし、外観上MySQLServerと一致すれば、MySQL JDBCおよびその他MySQLProtocolベースのドライバー(例:MaraiDB 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は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で最も一般的に使用されるインターフェースの1つで、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パラメータは、この機能を有効または無効にする役割を果たします。
プレペア時に、OBCrc32C(CRC32チェックサムアルゴリズムの純Java実装であり、CRC32-C多項式を使用する。iSCSIで使用されている多項式は、SSE4.2をサポートする多くのIntelチップセットで実装されているものと同じである)を用いてチェックサムを計算し、その後のすべてのExecuteメッセージに含めて送信します。チェックサムに誤りがある場合、OBServerノードはエラーを報告します。
詳細を見る
OceanBase Connector/Jの詳細および使用方法については、『OceanBase Connector/J』を参照してください。