4 JDBCアプリケーションとODBCアプリケーションのSQL翻訳
JDBCおよびODBCアプリケーションで使用できるSQL翻訳メカニズムが用意されています。
JDBCアプリケーションのSQL翻訳
JDBCアプリケーションでのSQLトランスレータの使用方法を理解するうえで必要な概念を詳しく見ていきます。
SQL翻訳プロファイル
SQL翻訳プロファイルは、Oracle以外の言語のSQL文をどのようにOracle SQL言語に翻訳するかを指示するデータベース・スキーマ・オブジェクトです。また、Oracleエラー・コードとSQLSTATES
が、他のベンダーのSQL言語に翻訳される方法も指示します。
Oracle以外のSQLデータベース用に作成されたクライアント・アプリケーションをOracleに移行する場合、SQL翻訳プロファイルを作成し、アプリケーションのSQL文とエラーを翻訳するようにそのプロファイルを構成できます。アプリケーションは、そのSQL文とエラーを翻訳するためにOracle Databaseで接続のプロファイルをランタイムで設定します。このプロファイルは、oracle.jdbc.sqlTranslationProfile
プロパティを使用して設定されます。
必要に応じて、SQL文とエラーのカスタム翻訳を、サーバーのSQL翻訳プロファイルに登録できます。SQL文やエラーが翻訳されると、最初にカスタム翻訳が調べられ、一致が見つからない場合のみ、トランスレータが起動します。
「SQL翻訳フレームワークのアーキテクチャ」および「SQL翻訳プロファイルの設定」を参照してください。
エラー・メッセージの翻訳
ネイティブ・データベースによってスローされる場合に使用されるメッセージの形式で、エラー・メッセージを受信できます。データベースへの有効な接続がない場合は、エラー・メッセージ翻訳ファイルを使用してエラー・メッセージを翻訳する必要があります。データベースへの接続が確立されると、JDBCドライバはこのファイルを完全にバイパスし、すべてのエラーはサーバー上のトランスレータを使用して行われます。問合せの翻訳と同様に、サーバーにカスタム・エラー翻訳を登録することもできます。
エラー・メッセージ翻訳ファイルは、特定のコンポーネントによって記述されるわけではありません。翻訳するファイルを提供し、ファイル名を指定する必要があります。対応する接続プロパティの値としてファイル・パスを指定することもできます。
エラー・メッセージ翻訳ファイルはXML形式であり、ファイルには一連のエラーの翻訳が含まれています。各エラー翻訳には、次の情報が含まれます。
翻訳エラー | タイプ |
---|---|
ORAエラー番号 |
正の整数 |
Oracleエラー・メッセージ |
文字列 |
翻訳されたエラー・コード |
正の整数 |
翻訳されたSQL状態 |
正の整数 |
JDBC標準パラメータ・マーカー
JDBCドライバは、SQL文を発行して翻訳する前に、内部でJDBC標準パラメータ・マーカー(?
)をOracle形式のパラメータ・マーカー(:b<n>
形式)に変換します。
ここで、パラメータ・マーカーのネーミング形式は:b<n>
で、n
はJDBC PreparedStatement
の(?
)マーカーの位置を示す増分数です。
UPDATE employees SET salary = salary * ? WHERE employee_id = ?
PreparedStatement
文の場合、最初のパラメータ・マーカー(?
)は:b1
、2番目のパラメータ・マーカー(?
)は:b2
になります。
変換後、ドライバは次の問合せをサーバーに送信して、翻訳を行います。
UPDATE employees SET salary = salary * :b1 WHERE employee_id = :b2
パラメータ・マーカーとしての"?"を含む問合せは、processEscapes
プロパティの値をFALSE
に変更すると、接続翻訳フェーズで失敗することに注意してください。翻訳が正常に行われるようにするには、processEscapes
プロパティのデフォルト値のままにしておく必要があります。
パラメータ・マーカーの変換を行うと、翻訳時に発生したパラメータの変更をドライバが自動的に並び替える場合に役立ちます。変換時に、サーバーにカスタム翻訳を登録する必要がある場合は、Oracle形式のパラメータ・マーカーのバージョンから登録してください。そのバージョンで、サーバーは文を受信します。カスタム翻訳には、元の問合せと同じ数のOracle形式のパラメータ・マーカーが必要なことに注意してください。
サポートされているJDBC APIの詳細は、「JDBCアプリケーションのSQL翻訳のAPIリファレンス」を参照してください。
翻訳済Oracle言語の問合せの実行
JDBC標準パラメータ・マーカーがOracleスタイルのパラメータ・マーカーに変換されると、ドライバは、問合せをOracle言語に翻訳するためにサーバーに対してラウンドトリップを行います。翻訳された問合せをサーバーが受信すると、パラメータの並替えがドライバによって透過的に処理され、問合せは通常の問合せとして実行されます。
翻訳ができないために問合せの翻訳が不可になる場合、サーバーは、DBMS_SQL_TRANSLATOR.ATTR_RAISE_TRANSLATION_ERROR
プロファイル属性の値に基づき、エラーを発生するか、NULL
を戻すことができます。サーバーがNULL
を戻す場合、翻訳されていない元の問合せが、ドライバによって翻訳される問合せとみなされて実行されます。
ドライバは、ローカル・キャッシュに翻訳を保持して、今後のラウンドトリップを保存します。
JDBCドライバは、DBMS_SQL_TRANSLATOR.ATTR_RAISE_TRANSLATION_ERROR
属性のいずれかの値により設定される翻訳エラー(翻訳ができないために問合せが翻訳できない場合)をサポートできます。ただし、この値は、接続が確立される前にサーバー上で設定する必要があります。セッション中にこの属性の値を変更すると、動作の一貫性が保たれない場合があるので、セッション中はこの属性の値を反転しないことをお薦めします。TRANSLATE_SQL
プロシージャの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。
エラー翻訳
SQLException
が問合せ実行時にスローされた場合、ドライバは、サーバーに透過的に移動し、例外をOracleコードから元のベンダー固有のコードに翻訳します。結果として生じるSQLException
には、ベンダー固有のコードとSQLSTATE
、およびOracle固有のSQLException
が原因として存在します。
問合せ翻訳と同様に、サーバーにカスタム・エラー翻訳を登録して、その翻訳を標準翻訳よりも優先にすることもできます。DBMS_SQL_TRANSLATOR.ATTR_RAISE_TRANSLATION_ERROR
属性のカスタム・エラー翻訳と問合せ翻訳に対する効果は同じです。
エラーは、サーバーへの接続が確立された後でしか翻訳されないことに注意してください。そのため、サーバーへの接続が確立される前に発生するエラーの場合、エラー・メッセージの翻訳が使用されます。
JDBCドライバを使用したSQL翻訳
例4-1に、SQL翻訳でのJDBCドライバの使用方法を示します。最初に、次のようにCREATE SQL TRANSLATION PROFILE
権限をHR
に付与する必要があります。
conn system/manager; grant create sql translation profile to HR; exit
HR
としてデータベースに接続し、次のSQL文を実行します。
drop table sample_tab; create table sample_tab (c1 number, c2 varchar2(100)); insert into sample_tab values (1, 'A'); insert into sample_tab values (1, 'A'); insert into sample_tab values (1, 'A'); commit; exec dbms_sql_translator.drop_profile('FOO'); exec dbms_sql_translator.create_profile('FOO'); exec dbms_sql_translator.register_sql_translation('FOO','select row of select c1, c2 from sample_tab where c1=:b1 and c2=:b2','select c1, c2 from sample_tab where c1=:b1 and c2=:b2');
JDBC標準パラメータ・マーカーを使用するSQL文を翻訳する次のプログラムを実行できます。
例4-1 JDBCドライバを使用したOracle以外のSQL文のOracle SQL言語への翻訳
public class SQLTransPstmt
{
static String url="jdbc:oracle:thin:@localhost:5521:jvx1";
static String user="HR", pwd="hr";
static String PROFILE = "FOO";
static String primitiveSql = "select row of select c1, c2 from sample_tab where c1=? and c2=?";
// Note that this query contains JDBC style parameter markers
// But the preceding custom translation registered in SQL is using Oracle style markers
public static void main(String[] args) throws Exception
{
OracleDataSource ods = new OracleDataSource();
ods.setURL(url);
Properties props = new Properties();
props.put("user", user);
props.put("password", pwd);
// The Following connection property makes the connection translating
props.put(OracleConnection.CONNECTION_PROPERTY_SQL_TRANSLATION_PROFILE, PROFILE);
ods.setConnectionProperties(props);
Connection conn = ods.getConnection();
System.out.println("connection for SQL translation: "+conn);
try{
// Any statements created using a translating connection are
// automatically translating. If you want to get a non-translating
// statement out of a translating connection please have a look at
// the oracle.jdbc.OracleTranslatingConnection Interface.
// Refer to "OracleTranslatingConnection Interface"
// for more information
PreparedStatement trStmt = conn.prepareStatement(primitiveSql);
trStmt.setInt(1, 1);
trStmt.setString(2, "A");
System.out.println("executeQuery for: "+primitiveSql);
ResultSet trRs = trStmt.executeQuery();
while (trRs.next())
System.out.println("C1:"+trRs.getInt(1)+", C2:"+trRs.getString(2));
trRs.close();
trStmt.close();
}catch (Exception e) {
e.printStackTrace();
}
conn.close();
}
}
ODBCアプリケーションのSQL翻訳
ODBCアプリケーションでのSQLトランスレータの使用方法を理解するうえで必要な概念を詳しく見ていきます。
エラー・メッセージの翻訳
ネイティブ・データベースによってスローされる場合に使用されるメッセージの形式で、エラー・メッセージを受信できます。そのような場合、アプリケーションがOracle Databaseで実行されるよう設定されている際に、ORA
エラーをネイティブ・フォームに翻訳するには、.odbc.ini
ファイルにSQLTranslateErrors=T
エントリを設定する必要があります。
エラー・メッセージの翻訳
例4-2に、SQL翻訳でのODBCドライバの使用方法を示します。この例で使用されるSQL文ではSybase TOP N
構文を使用します。
「SQL翻訳フレームワークの使用方法」の項で作成したデータベース・サービス名を使用して、.odbc.ini
ファイルにServerName=
エントリを設定する必要があることに注意してください。ネイティブ・データベース・エラーへのOracleエラーの変換が必要な場合は、.odbc.ini
ファイルにSQLTranslateErros=T
エントリも設定します。
例4-2 ODBCドライバを使用したOracle以外のSQLのOracle SQL言語への翻訳
int main() { HENV m_henv; /* environment handle */ HDBC m_hdbc; /* connection handle */ HSTMT m_hstmt; /* statement handle */ int retCode; /* return code */ char dbdsn[100]; /* Initialize with the DSN name of connection */ const char szUID[10];/*Initialize with appropriate Username of DB */ const char szPWD[10]; /* Initialize with appropriate Password */ char query1[100]="select top 3 col1 from babel_tab3 order by col1"; SQLLEN paramInd = SQL_NTS; SQLUINTEGER no = 0; //Allocate HENV, HDBC, HSTMT handles retCode = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_henv); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLAllocHandle failed \n"); printSQLError (1, m_henv); } retCode = SQLSetEnvAttr (m_henv, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, SQL_IS_INTEGER); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLSetEnvAttr failed\n"); printSQLError (1, m_henv); } retCode = SQLAllocHandle (SQL_HANDLE_DBC, m_henv, &m_hdbc); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLAllocHandle failed\n"); printSQLError (2, m_hdbc); } retCode = SQLConnect (m_hdbc, (SQLCHAR *) dbdsn,SQL_NTS, (SQLCHAR *) szUID, SQL_NTS, (SQLCHAR *) szPWD, SQL_NTS); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLConnect failed to connect\n"); printSQLError (2, m_hdbc); } retCode = SQLAllocHandle (SQL_HANDLE_STMT, m_hdbc, &m_hstmt); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLAllocHandle with SQL_HANDLE_STMT failed\n"); printSQLError (3, m_hstmt); } /* Prepare and Execute the Sybase Top-N syntax SQL statements */ retCode = SQLPrepare (m_hstmt, (SQLCHAR *) query1, SQL_NTS); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLPrepare failed\n"); printSQLError (3, m_hstmt); } retCode=SQLExecute(m_hstmt); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLExecute-failed\n"); printSQLError (3, m_hstmt); } while (retCode = SQLFetch(m_hstmt)!=SQL_NO_DATA) { retCode=SQLGetData(m_hstmt,1,SQL_C_ULONG, &no, 0, ¶mInd); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLFetch failed\n"); printSQLError (3, m_hstmt); } printf("Value is %d\n",no); } retCode = SQLCloseCursor (m_hstmt); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) printf ("SQLCloseCursor failed\n"); printf ("cleanup()\n"); retCode = SQLFreeHandle (SQL_HANDLE_STMT, m_hstmt); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLFreeHandle failed\n"); printSQLError (3, m_hstmt); } retCode = SQLDisconnect (m_hdbc); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLDisconnect failed\n"); printSQLError (2, m_hdbc); } retCode = SQLFreeHandle (SQL_HANDLE_DBC, m_hdbc); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLFreeHandle failed\n"); printSQLError (2, m_hdbc); } retCode = SQLFreeHandle (SQL_HANDLE_ENV, m_henv); if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) { printf ("SQLFreeHandle failed\n"); printSQLError (1, m_henv); } }