データベースエラーの処理

Adobe AIR 1.0 およびそれ以降

一般に、データベースエラーの処理は他のランタイムエラーの処理と同様で、発生する可能性があるエラーに備えたコードを記述して、ランタイムではなくそのコードがエラーに応答するようにします。発生する可能性があるデータベースエラーは、一般的な意味において 3 つのカテゴリに分類できます。接続エラー、SQL シンタックスエラーおよび制約エラーの 3 つです。

接続エラー

ほとんどのデータベースエラーは接続エラーです。接続エラーはあらゆる操作で発生します。接続エラーを防止するための対策はありますが、データベースがアプリケーションの重要な部分である場合、接続エラーから速やかに回復するための簡単な方法はほとんどありません。

ほとんどの接続エラーは、ランタイムとオペレーティングシステム、ファイルシステムおよびデータベースファイルとのやり取りに関連しています。例えば、ファイルシステム上の特定の場所にデータベースファイルを作成する権限がユーザーにないと、接続エラーが発生します。接続エラーを防止するための対策を以下に示します。

ユーザー固有のデータベースファイルを使用する
1 つのコンピューターでアプリケーションを使用するすべてのユーザーに対して 1 つのデータベースファイルを使用する代わりに、各ユーザーに専用のデータベースファイルを割り当てます。そのファイルは、ユーザーのアカウントに関連付けられているディレクトリに配置します(アプリケーションの格納ディレクトリ、ユーザーのドキュメントフォルダー、ユーザーのデスクトップなど)。

様々なユーザーの種類を考慮に入れる
様々なオペレーティングシステムで様々な種類のユーザーアカウントを使用してアプリケーションをテストします。ユーザーがコンピューターの管理者権限を持っているとは限りませんし、アプリケーションをインストールしたユーザーがアプリケーションを実行するとも限りません。

様々なファイルの場所を考慮に入れる
ユーザーがデータベースファイルの保存場所を指定したり、開くファイルを選択したりできるようにする場合は、ユーザーによって使用される可能性があるファイルの場所について検討します。さらに、ユーザーがデータベースファイルを格納できる場所(またはデータベースファイルを開くことができる場所)を制限することも検討します。例えば、自分のユーザーアカウントの格納場所にあるファイルのみを開けるようにすることができます。

接続エラーが発生する可能性が最も高いのは、データベースを最初に作成したり開いたりするときです。したがって、接続エラーが発生すると、ユーザーがアプリケーションでデータベース関連の操作を一切実行できなくなります。読み取り専用エラーや権限エラーなどの一部の種類のエラーに対しては、データベースファイルを別の場所にコピーするという方法でエラーから回復できます。アプリケーションで、ユーザーがファイルの作成や書き込みの権限を持っている別の場所にデータベースファイルをコピーして、その場所を代わりに使用します。

シンタックスエラー

シンタックスエラーは、形式が正しくない SQL ステートメントをアプリケーションが実行しようとすると発生します。ローカルデータベースの SQL ステートメントは文字列として作成されるため、コンパイル時に SQL シンタックスをチェックすることはできません。SQL ステートメントのシンタックスをチェックするには、そのステートメントを実行する必要があります。SQL シンタックスエラーを防止するための対策を以下に示します。

すべての SQL ステートメントを徹底的にテストする
できれば、アプリケーションの開発時(SQL ステートメントをステートメントテキストとしてアプリケーションコードに含める前)に別途 SQL ステートメントのテストを行います。さらに、単体テストなどのコードテスト手法を使用して、コードのすべてのオプションやバリエーションを実行するテストセットを作成します。

SQL を連結する(動的に生成する)代わりにステートメントパラメーターを使用する
SQL ステートメントを動的に作成する代わりにパラメーターを使用すると、毎回同じ SQL ステートメントテキストを使用してステートメントが実行されることになります。その結果、ステートメントのテストが格段に容易になり、コードのバリエーションも限定されます。SQL ステートメントを動的に生成する必要がある場合は、ステートメントの動的な部分を最小限に抑えます。また、シンタックスエラーを引き起こさないようにユーザー入力を慎重に検証します。

アプリケーションでシンタックスエラーから回復するには、SQL ステートメントを調べてシンタックスを修正するための複雑なロジックが必要になります。シンタックスエラーを防止するための上述のガイドラインに従うと、実行時に SQL シンタックスエラーを引き起こす可能性がある場所(ステートメント内で使用されるユーザー入力など)をコードで特定できます。シンタックスエラーから回復するには、ユーザーにアドバイスを提供して、ステートメントが正常に実行されるようにするにはどこを修正すればよいかを知らせます。

制約エラー

制約エラーは、 INSERT ステートメントや UPDATE ステートメントでデータを列に追加するときに、そのテーブルまたは列に対して定義されている制約に新しいデータが違反していると発生します。制約には次のようなものがあります。

UNIQUE 制約
テーブルのすべての行で列の値が重複していてはいけないことを表します。複数の列が UNIQUE 制約で結合される場合は、それらの列の値の組み合わせが重複していてはいけないことになります。つまり、一意として指定された列では、各行がそれぞれ異なっている必要があります。

主キー制約
制約で許可されるデータと許可されないデータに関しては、主キー制約は UNIQUE 制約と同じです。

NOT NULL 制約
列に NULL 値を格納できないことを表します。したがって、すべての行でその列に値がなければならないことになります。

CHECK 制約
1 つ以上のテーブルに対して任意の制約を指定できます。CHECK 制約では、列の値が取りうる範囲を定義するルール(数値列の値が 0 より大きくなければならないなど)を指定したり、列の値の関係(一方の列の値がもう一方の列の同じ行の値と異なっていなければならないなど)を指定したりするのが一般的です。

データ型(列の類似性)の制約
列の値のデータ型が強制されて、正しくない型の値を列に格納しようとするとエラーが発生します。ただし、多くの場合は、宣言されている列のデータ型に合わせて値が変換されます。詳しくは、 データベースのデータ型の操作 を参照してください。

外部キーの値に対する制約は適用されません。したがって、外部キーの値が既存の主キーの値と一致している必要はありません。

ランタイムの SQL エンジンでは、あらかじめ定義されている種類の制約に加えて、トリガーを使用することもできます。トリガーとは、イベントハンドラーのように、特定のアクションが発生すると実行される定義済みの命令のセットです。例えば、特定のテーブルのデータが挿入されたり削除されたりした場合に実行されるトリガーを定義することができます。トリガーを使用すると、データの変更を調べて、指定した条件が満たされていない場合にエラーを発生させることもできます。したがって、トリガーは制約と同じ目的で使用することができます。制約エラーの防止と回復のための対策は、トリガーによって生成されるエラーにも当てはまります。ただし、トリガーによって生成されるエラーのエラー ID は制約エラーのエラー ID とは異なります。

特定のテーブルに適用する制約のセットは、アプリケーションの設計時に決定します。制約を意識的に設計すると、アプリケーションの設計で制約エラーの防止や回復が容易になります。ただし、制約エラーは体系的な予測や防止が困難です。制約エラーの予測が困難なのは、アプリケーションのデータが追加されるまで明らかにならないからです。制約エラーは、データベースの作成後、そこに追加されたデータによって発生します。それらのエラーは多くの場合、新しいデータとデータベースの既存のデータとの関係に起因しています。多くの制約エラーの防止に役立つ対策を以下に示します。

データベースの構造と制約について入念に計画する
制約の目的は、アプリケーションのルールを適用してデータベースのデータの整合性を保護することです。アプリケーションの計画を立てる際には、アプリケーションをサポートするために必要なデータベースの構造について検討し、その一環としてデータのルールを特定します(特定の値が必要かどうか、値に既定値があるかどうか、重複する値を許可するかどうかなど)。それらのルールに基づいてデータベースの制約を定義できます。

列の名前を明示的に指定する
INSERT ステートメントは、値を挿入する列を明示的に指定しなくても記述できますが、無駄にリスクを高めることになります。値を挿入する列の名前を明示的に指定すると、自動的に生成される値、既定値を持つ列、 NULL 値を許容する列などを使用できるようになります。また、すべての NOT NULL 列に明示的な値が挿入されていることを確認することもできます。

既定値を使用する
列に対して NOT NULL 制約を指定する場合は、可能であれば列定義で既定値を指定します。アプリケーションコードで既定値を指定することもできます。例えば、String 変数を使用してステートメントのパラメーター値を設定する前に、その String 変数が null かどうかをコードでチェックして値を割り当てることができます。

ユーザーが入力したデータを検証する
特に NOT NULL 制約と CHECK 制約では、ユーザーが入力したデータを事前にチェックして、制約によって指定されている制限に従っているかどうかを確認します。 UNIQUE 制約では、このチェックを行うには SELECT クエリを実行してデータが一意かどうかを確認する必要があるため、必然的に他の制約に比べてチェックが困難になります。

トリガーを使用する
トリガーを記述すると、挿入されたデータを検証(および場合によっては置換)したり、正しくないデータを修正するためのその他のアクションを実行したりできます。この検証と修正によって制約エラーの発生を防止できます。

制約エラーは様々な点で他の種類のエラーに比べて防止が困難ですが、幸い、アプリケーションが不安定になったり使用できなくなったりしないような形で制約エラーから回復するための方法がいくつかあります。

競合アルゴリズムを使用する
列に対して制約を定義した場合は、 INSERT ステートメントや UPDATE ステートメントを作成するときに競合アルゴリズムを指定することができます。競合アルゴリズムは、制約違反が発生した場合にデータベースで実行されるアクションを定義します。データベースエンジンで実行できるアクションはいくつかあります。例えば、1 つのステートメントを終了する、トランザクション全体を終了する、エラーを無視するなどのほか、古いデータを削除して、格納しようとしているデータに置き換えることもできます。

詳しくは、 ローカルデータベースでの SQL サポート の「ON CONFLICT(競合アルゴリズム)」を参照してください。

修正のためのフィードバックを提供する
特定の SQL コマンドに影響を与える可能性がある制約のセットは事前に特定できるため、ステートメントで発生する可能性がある制約エラーを予測することができます。その知識に基づいて、制約エラーに応答するアプリケーションロジックを作成できます。例えば、新しい製品を入力するためのデータ入力フォームを含むアプリケーションで、データベースの製品名列の定義で UNIQUE 制約を使用すると、データベースに新しい製品行を挿入するアクションで制約エラーが発生する可能性があります。したがって、このアプリケーションは、制約エラーを予測する形で設計されます。エラーが発生したら、指定した製品名は既に使用されているという内容の警告メッセージをユーザーに表示して、別の名前を選択するように求めます。あるいは、同じ名前を持つ他の製品に関する情報をユーザーが表示できるようにすることもできます。