| Oracle® Fusion Middleware Oracle Application Development Frameworkモバイル開発者ガイド 11g リリース2 (11.1.2.4.0) B70750-02 |
|
![]() 前 |
![]() 次 |
この章では、ADFモバイル・アプリケーションを使用したローカルSQLiteデータベースの使用方法について説明します。
この章には次の項が含まれます:
SQLiteは、特に埋込みアプリケーション用に設計されたリレーショナル・データベース管理システム(RDBMS)です。
SQLiteには次の特性があります。
ACID準拠: 他の従来のデータベース・システムと同様に、原子性、一貫性、独立性および永続性という特性を備えています。
軽量: アプリケーション内に直接埋め込むように設計された小さなCライブラリでデータベース全体が構成されています。
移植性: 様々な範囲のコンピュータ・アーキテクチャおよびオペレーティング・システム間でバイナリ互換性がある単一ファイルでデータベースは自己完結型となります。
詳細は、SQLite Webサイト(http://www.sqlite.org)を参照してください。
ローカルSQLiteデータベースの使用例は、HRと呼ばれるADFモバイルのサンプル・アプリケーションを参照してください。ここでのデータは、すべてのOracleデータベースで提供されるデフォルトのHRスキーマに基づいています。データは、ローカルSQLiteデータベースに格納されており、各起動間で持続されています。HRアプリケーションは、開発コンピュータのjdev_install/jdeveloper/jdev/extensions/oracle.adf.mobile/Samplesディレクトリ内にあるPublicSamples.zipファイルに配置されています。
SQLiteは、埋込みデータベース・システムとして使用する設計になっており、通常は1人のユーザーによって使用され、アプリケーションに直接リンクされることも多くあります。他方、エンタープライズ・データベースは、クライアントとサーバーの分散された環境における高い同時実行性を求めて設計されています。このような違いがあるため、Oracleデータベースと比較して複数の制約が存在します。最も重要な相違点を次に示します。
詳細は、次を参照してください:
SQLite Webサイト(http://www.sqlite.org/docs.html)のドキュメント・セクション
SQLite Webサイトのドキュメント・セクションで入手可能な『Limits In SQLite』(http://www.sqlite.org/limits.html)
SQLiteデータベースの単一インスタンスは、常に、単一の読取り/書込み接続または複数の読取り専用接続のいずれかを持つことができます。
ロック・メカニズムが粗いため、SQLiteでは、同一のデータベース・インスタンスに対する複数の読取り/書込み接続がサポートされていません。詳細は、SQLite Webサイトのドキュメント・セクションで入手可能な『File Locking And Concurrency In SQLite Version 3』(http://www.sqlite.org/lockingv3.html)を参照してください。
SQLiteはSQL92標準でコンパイルされますが、サポートされていない構成が次を含めていくつか存在します。
RIGHT OUTER JOIN
FULL OUTER JOIN
GRANT
REVOKE
詳細は、SQLite Webサイトのドキュメント・セクションで入手可能な『SQL Features That SQLite Does Not Implement』(http://www.sqlite.org/omitted.html)を参照してください。
SQLiteによるSQLの解釈の方法については、SQLite Webサイトのドキュメント・セクションで入手可能な『SQL As Understood by SQLite』(http://www.sqlite.org/lang_createtable.html)を参照してください。
大部分のデータベース・システムは強く型指定をしているのに対して、SQLiteは動的な型指定のため、宣言された型とは無関係にどの列にどの値を格納することもできます。たとえば、数値列に文字列値が間違って格納された場合でも、SQLiteではエラーは戻されません。詳細は、SQLite Webサイトのドキュメント・セクションで入手可能な『Datatypes In SQLite Version 3』(http://www.sqlite.org/datatype3.html)を参照してください。
SQLiteはACID準拠であるためトランザクションをサポートしていますが、SQLiteとOracleのトランザクション・サポートの間には基本的な相違点がいくつか存在します。
ネストされたトランザクション: SQLiteでは、ネストされたトランザクションをサポートしません。単一のトランザクションのみを常時アクティブにできます。
コミット: SQLiteは、任意の指定されたデータベースに対し、複数の読取り専用接続または単一の読取り/書込み接続のいずれかを許容します。したがって、同一のデータベースに対して複数の接続がある場合は、データベースの変更を試みる最初の接続のみが成功します。
ロールバック: SQLiteでは、開いているすべてのResultSetsを閉じるまではトランザクションのロールバックができません。
詳細は、SQLite Webサイトのドキュメント・セクションで入手可能な『Distinctive Features of SQLite』(http://www.sqlite.org/different.html)を参照してください。
SQLiteでは、ロールベースまたはユーザーベースの認証はどの形式もサポートしていません。デフォルトで、どのユーザーもファイル内のすべてのデータにアクセスできます。ただし、ADFモバイルでは、使用可能な暗号化ルーチンを提供することで、データを保護し、有効な資格証明セットを持たないユーザーによるアクセスを防ぎます。
ADFモバイルには、暗号化されたSQLiteデータベースが含まれています。通常SQLiteを使用する場合は、次のことを知っておく必要があります。
SQLiteデータベースへの接続は、Oracleデータベースへの接続を開く場合とは少し異なります。つまり、最初の接続を取得すると、同じJDBC APIおよびSQL構文の大部分が使用可能になり、データベースの問合せと変更が可能になります。
ユーザーのアプリケーションに関連付けられたjava.sql.Connectionオブジェクトを使用して、SQLiteデータベースに接続します。接続を作成する場合は、どのSQLite JDBC URLもjdbc:sqlite:というテキストで始まるようにします。
例11-1は、暗号化されていないデータベースと接続を開く方法を示しています。
例11-1 暗号化されていないデータベースとの接続
java.sql.Connection connection = new SQLite.JDBCDataSource
("jdbc:sqlite:/path/to/database").getConnection();
例11-2は、暗号化されたデータベースと接続を開く方法を示しています。
例11-2 暗号化されたデータベースとの接続
java.sql.Connection connection = new SQLite.JDBCDataSource
("jdbc:sqlite:/path/to/database").getConnection(null,"password");
前述の例では、getConnectionメソッドの最初のパラメータがユーザー名となっていますが、SQLiteではユーザーベースのセキュリティがサポートされないため、この値は無視されます。
|
注意: SQLiteでは、不正なパスワードで暗号化されたデータベースを開いた場合でも、エラー・メッセージが表示されません。同様に、暗号化されていないデータベースをパスワードで誤って開いた場合でも警告は表示されません。そのかわりに、データの読取りまたは変更を試みた場合は、エラー: ファイルが暗号化されているか、ファイルがデータベースではありません。というメッセージとともに |
通常、アプリケーションの起動時にSQLスクリプトを使用してデータベースを初期化できます。例11-3は、サポートされたSQL構文(第11.1.1.2項「SQLのサポートおよび解釈」で説明)の一部を示すSQL初期化スクリプトを示しています。この構文では、DROP TABLE、CREATE TABLEおよびINSERTの各コマンドとNUMBERおよびVARCHAR2の各データ型が使用されています。
例11-3 SQL初期化スクリプト
DROP TABLE IF EXISTS PERSONS; CREATE TABLE PERSONS ( PERSON_ID NUMBER(15) NOT NULL, FIRST_NAME VARCHAR2(30), LAST_NAME VARCHAR2(30), EMAIL VARCHAR2(25) NOT NULL ); INSERT INTO PERSONS (PERSON_ID, FIRST_NAME, LAST_NAME, EMAIL) VALUES ( 100, 'David', 'King', 'steven@king.net'); INSERT INTO PERSONS (PERSON_ID, FIRST_NAME, LAST_NAME, EMAIL) VALUES ( 101, 'Neena', 'Kochhar', 'neena@kochhar.net'); INSERT INTO PERSONS (PERSON_ID, FIRST_NAME, LAST_NAME, EMAIL) VALUES ( 102, 'Lex', 'De Haan', 'lex@dehaan.net'); INSERT INTO PERSONS (PERSON_ID, FIRST_NAME, LAST_NAME, EMAIL) VALUES ( 103, 'Alexander', 'Hunold', 'alexander@hunold.net'); INSERT INTO PERSONS (PERSON_ID, FIRST_NAME, LAST_NAME, EMAIL) VALUES ( 104, 'Bruce', 'Ernst', 'bruce@ernst.net');
SQLスクリプトを使用するには、それをADFモバイル・アプリケーションのApplicationControllerプロジェクトにリソースとして追加します。サンプル・スクリプトは、META-INFディレクトリでinitialize.sqlとして保存されているものとします。例11-4は、SQLスクリプトを解析し、その文を実行するために追加する必要のあるコードが示されています。
例11-4 SQLスクリプトからのデータベースの初期化
private static void initializeDatabaseFromScript() throws Exception {
InputStream scriptStream = null;
Connection conn = null;
try {
// ApplicationDirectory returns the private read-write sandbox area
// of the mobile device's file system that this application can access.
// This is where the database is created
String docRoot = AdfmfJavaUtilities.getDirectoryPathRoot
(AdfmfJavaUtilities.ApplicationDirectory);
String dbName = docRoot + "/sample.db";
// Verify whether or not the database exists.
// If it does, then it has already been initialized
// and no furher actions are required
File dbFile = new File(dbName);
if (dbFile.exists())
return;
// If the database does not exist, a new database is automatically
// created when the SQLite JDBC connection is created
conn = new SQLite.JDBCDataSource("jdbc:sqlite:" + docRoot +
"/sample.db").getConnection();
// To improve performance, the statements are executed
// one at a time in the context of a single transaction
conn.setAutoCommit(false);
// Since the SQL script has been packaged as a resource within
// the application, the getResourceAsStream method is used
scriptStream = Thread.currentThread().getContextClassLoader().
getResourceAsStream("META-INF/initialize.sql");
BufferedReader scriptReader = new BufferedReader
(new InputStreamReader(scriptStream));
String nextLine;
StringBuffer nextStatement = new StringBuffer();
// The while loop iterates over all the lines in the SQL script,
// assembling them into valid SQL statements and executing them as
// a terminating semicolon is encountered
Statement stmt = conn.createStatement();
while ((nextLine = scriptReader.readLine()) != null) {
// Skipping blank lines, comments, and COMMIT statements
if (nextLine.startsWith("REM") ||
nextLine.startsWith("COMMIT") ||
nextLine.length() < 1)
continue;
nextStatement.append(nextLine);
if (nextLine.endsWith(";")) {
stmt.execute(nextStatement.toString());
nextStatement = new StringBuffer();
}
}
conn.commit();
}
finally {
if (conn != null)
conn.close();
}
}
例11-5で示すとおり、LifeCycleListenerImplのstartメソッドからデータベース初期化コード(例11-4を参照)を起動します。
SQLiteデータベースは自己完結型でプラットフォーム間のバイナリ互換性があるため、同じデータベース・ファイルをiOS、Android、Windows、LinuxおよびMac OSの各プラットフォームで使用できます。複雑なケースでは、サード・パーティのツール(MesaSQLite、SQLiteManager、SQLite Database Browserなど)を使用してデスクトップ上でデータベースを初期化し、結果ファイルをアプリケーション内のリソースとしてパッケージ化できます。
データベースを使用するには、それをADFモバイル・アプリケーションのApplicationControllerプロジェクトにリソースとして追加します。データベースは、META-INFディレクトリにsample.dbとして保存されているものとします。例11-6は、アプリケーションからモバイル・デバイスのファイル・システムにデータベースをコピーしてデータベースへのアクセスを有効化するために追加する必要のあるコードを示しています。
例11-6 デスクトップ上のデータベースの初期化
private static void initializeDatabase() throws Exception {
InputStream sourceStream = null;
FileOutputStream destinationStream = null;
try {
// ApplicationDirectory returns the private read-write sandbox area
// of the mobile device's file system that this application can access.
// This is where the database is created
String docRoot = AdfmfJavaUtilities.getDirectoryPathRoot
(AdfmfJavaUtilities.ApplicationDirectory);
String dbName = docRoot + "/sample.db";
// Verify whether or not the database exists.
// If it does, then it has already been initialized
// and no furher actions are required
File dbFile = new File(dbName);
if (dbFile.exists())
return;
// Since the database has been packaged as a resource within
// the application, the getResourceAsStream method is used
scriptStream = Thread.currentThread().getContextClassLoader().
getResourceAsStream("META-INF/sample.db");
destinationStream = new FileOutputStream(dbName);
byte[] buffer = new byte[1000];
int bytesRead;
while ((bytesRead = sourceStream.read(buffer)) != -1) {
destinationStream.write(buffer, 0, bytesRead);
}
}
finally {
if (sourceStream != null)
sourceStream.close();
if (destinationStream != null)
destinationStream.close();
}
}
例11-7で示すとおり、LifeCycleListenerImplのstartメソッドからデータベース初期化コード(例11-6を参照)を起動します。
java.sqlパッケージからの次のメソッドは、ADFモバイル内では制限があるか、サポートされていません。
ResultSetのgetByteメソッドはサポートされていません。このメソッドを使用した場合は、実行時にメソッドからSQLExceptionがスローされます。
(ResultSetを戻す文に対してのみtrueを戻すのとは対照的に)Statementのexecuteメソッドは常にtrueを戻します。
ADFモバイルでは、初期またはそれ以降に暗号化したSQLiteデータベースを提供できます。これを行うには、データベースの接続を確立し(第11.2.1項「データベースへの接続方法」を参照)、次のユーティリティ・メソッドを使用して新しい鍵でデータベースを暗号化する必要があります。
AdfmfJavaUtilities.encryptDatabase(connection, "newPassword");
|
注意: データベースを不正に開いて(不正なパスワードを使用して暗号化されたデータベースを開くなど)、再び暗号化した場合は、以前の正しいパスワード、不正なパスワード、新しいパスワードのいずれでもデータベースのロックを解除できなくなり、取り返しのつかないデータの損失につながります。 |
暗号化に加えて、データベースを永続的に復号化することもできます。これを行うには、暗号化されたデータベースを正しいパスワードで開いて、次のメソッドを使用します。
AdfmfJavaUtilities.decryptDatabase(connection);