ヘッダーをスキップ
Oracle® Fusion Middleware Oracle Application Development Frameworkモバイル開発者ガイド
11g リリース2 (11.1.2.3.0)
B70750-01
  目次へ
目次

前
 
次
 

11 ローカル・データベースの使用方法

この章では、ADFモバイル・アプリケーションを使用したローカルSQLiteデータベースの使用方法について説明します。

この章では、次の項目について説明します。

11.1 ローカルSQLiteデータベースの使用方法の概要

SQLiteは、特に埋込みアプリケーション用に設計されたリレーショナル・データベース管理システム(RDBMS)です。

SQLiteには次の特性があります。

詳細は、http://www.sqlite.orgを参照してください。

ローカルSQLiteデータベースの使用例は、HRと呼ばれるADFモバイルのサンプル・アプリケーションを参照してください。ここでのデータは、すべてのOracleデータベースで提供されるデフォルトのHRスキーマに基づいています。データは、ローカルSQLiteデータベースに格納されており、各起動間で持続されています。HRアプリケーションは、開発コンピュータのjdev_install/jdeveloper/jdev/extensions/oracle.adf.mobile/Samplesディレクトリ内にあるPublicSamples.zipファイルに配置されています。

11.1.1 SQLiteと他のリレーショナル・データベースとの相違点

SQLiteは、埋込みデータベース・システムとして使用する設計になっており、通常は1人のユーザーによって使用され、アプリケーションに直接リンクされることも多くあります。他方、エンタープライズ・データベースは、クライアントとサーバーの分散された環境における高い同時実行性を求めて設計されています。このような違いがあるため、Oracleデータベースと比較して複数の制約が存在します。最も重要な相違点を次に示します。

詳細は、http://www.sqlite.org/docs.htmlにあるSQLiteサイトのドキュメント・セクションを参照してください。

11.1.1.1 同時実行性

SQLiteデータベースの単一インスタンスは、常に、単一の読取り/書込み接続または複数の読取り専用接続のいずれかを持つことができます。

ロック・メカニズムが粗いため、SQLiteでは、同一のデータベース・インスタンスに対する複数の読取り/書込み接続がサポートされていません。詳細は、http://www.sqlite.org/lockingv3.htmlにあるSQLiteサイトのドキュメント・セクションで入手可能な『File Locking And Concurrency In SQLite Version 3』を参照してください。

11.1.1.2 SQLのサポートおよび解釈

SQLiteはSQL92標準でコンパイルされますが、サポートされていない構成が次を含めていくつか存在します。

  • RIGHT OUTER JOIN

  • FULL OUTER JOIN

  • GRANT

  • REVOKE

詳細は、http://www.sqlite.org/omitted.htmlにあるSQLiteサイトのドキュメント・セクションで入手可能な『SQL Features That SQLite Does Not Implement』を参照してください。

SQLiteによるSQLの解釈の方法については、http://www.sqlite.org/lang_createtable.htmlにあるSQLiteサイトのドキュメント・セクションで入手可能な『SQL As Understood by SQLite』を参照してください。

11.1.1.3 データ型

大部分のデータベース・システムは強く型指定をしているのに対して、SQLiteは動的な型指定のため、宣言された型とは無関係にどの列にどの値を格納することもできます。たとえば、数値列に文字列値が間違って格納された場合でも、SQLiteではエラーは戻されません。詳細は、http://www.sqlite.org/datatype3.htmlにあるSQLiteサイトのドキュメント・セクションで入手可能な『Datatypes In SQLite Version 3』を参照してください。

11.1.1.4 データベース・トランザクション

SQLiteはACID準拠であるためトランザクションをサポートしていますが、SQLiteとOracleのトランザクション・サポートの間には基本的な相違点がいくつか存在します。

  • ネストされたトランザクション: SQLiteでは、ネストされたトランザクションをサポートしません。単一のトランザクションのみを常時アクティブにできます。

  • コミット: SQLiteでは、複数の読取り専用接続または単一の読取り/書込み接続のいずれかが常時可能です。したがって、同一のデータベースに対して複数の接続がある場合は、データベースの変更を試みる最初の接続のみが成功します。

  • ロールバック: SQLiteでは、開いているすべてのResultSetsを閉じるまではトランザクションのロールバックができません。

詳細は、http://www.sqlite.org/different.htmlにあるSQLiteサイトのドキュメント・セクションで入手可能な『Distinctive Features of SQLite』を参照してください。

11.1.1.5 認証

SQLiteでは、ロールベースまたはユーザーベースの認証はどの形式もサポートしていません。デフォルトで、どのユーザーもファイル内のすべてのデータにアクセスできます。ただし、ADFモバイルでは、使用可能な暗号化ルーチンを提供することで、データを保護し、有効な資格証明セットを持たないユーザーによるアクセスを防ぎます。

11.2 ローカルSQLiteデータベースの使用方法

通常SQLiteを使用する場合は、次のことを知っておく必要があります。

11.2.1 データベースへの接続方法

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では、不正なパスワードで暗号化されたデータベースを開いた場合でも、エラー・メッセージが表示されません。同様に、暗号化されていないデータベースをパスワードで誤って開いた場合でも警告は表示されません。そのかわりに、データの読取りまたは変更を試みた場合は、エラー: ファイルが暗号化されているか、ファイルがデータベースではありません。というメッセージとともにSQLExceptionがスローされます。


11.2.2 SQLスクリプトを使用してデータベースを初期化する方法

通常、アプリケーションの起動時にSQLスクリプトを使用してデータベースを初期化できます。例11-3は、サポートされたSQL構文(第11.1.1.2項「SQLのサポートおよび解釈」で説明)の一部を示すSQL初期化スクリプトを示しています。この構文では、DROP TABLECREATE 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-4では、わかりやすくするためにエラー処理が省略されました。


例11-5で示すとおり、LifeCycleListenerImplstartメソッドからデータベース初期化コード(例11-4を参照)を起動します。

例11-5 データベース初期化コードの起動

public void start() {
   try {
      initializeDatabaseFromScript();
   }
   catch (Exception e) {
      Trace.log(Utility.FrameworkLogger,
                Level.SEVERE, 
                LifeCycleListenerImpl.class, 
                "start", 
                e);
   }
}

11.2.3 デスクトップ上でデータベースを初期化する方法

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-6では、わかりやすくするためにエラー処理が省略されました。


例11-7で示すとおり、LifeCycleListenerImplstartメソッドからデータベース初期化コード(例11-6を参照)を起動します。

例11-7 データベース初期化コードの起動

public void start() {
   try {
      initializeDatabase();
   }
   catch (Exception e) {
      Trace.log(Utility.FrameworkLogger,
                Level.SEVERE, 
                LifeCycleListenerImpl.class, 
                "start", 
                e);
   }
}

11.2.4 コミット処理について

Commit文があってもこれは無視されます。各文は、SQLスクリプトから読み込まれる際にコミットされます。

11.2.5 ADFモバイルでのSQLite JDBCドライバの制限

java.sqlパッケージからの次のメソッドは、ADFモバイル内では制限があるか、サポートされていません。

  • ResultSetgetByteメソッドはサポートされていません。このメソッドを使用した場合は、実行時にメソッドからSQLExceptionがスローされます。

  • (ResultSetを戻す文に対してのみtrueを戻すのとは対照的に)Statementexecuteメソッドは常にtrueを戻します。

11.2.6 データベースを暗号化および復号化する方法

ADFモバイルでは、初期またはそれ以降に暗号化したSQLiteデータベースを提供できます。これを行うには、データベースの接続を確立し(第11.2.1項「データベースへの接続方法」を参照)、次のユーティリティ・メソッドを使用して新しい鍵でデータベースを暗号化する必要があります。

AdfmfJavaUtilities.encryptDatabase(connection, "newPassword");

注意:

データベースを不正に開いて(不正なパスワードを使用して暗号化されたデータベースを開くなど)、再び暗号化した場合は、以前の正しいパスワード、不正なパスワード、新しいパスワードのいずれでもデータベースのロックを解除できなくなり、取り返しのつかないデータの損失につながります。


暗号化に加えて、データベースを永続的に復号化することもできます。これを行うには、暗号化されたデータベースを正しいパスワードで開いて、次のメソッドを使用します。

AdfmfJavaUtilities.decryptDatabase(connection);