4

私の Android アプリケーションには、外部キーのデータベース関係に関連する奇妙な問題があります。次のコードは、私の単純なデータベースの構造を説明しています。データベース操作を処理するためのスーパークラスとして SQLiteOpenHelper を使用しました。

private static final String CATEGORIES_TABLE = "CREATE TABLE __CATEGORIES_TBL(_id INTEGER PRIMARY KEY AUTOINCREMENT, _name TEXT NOT NULL, _desc TEXT NULL,"
                    + "_create_date INTEGER NOT NULL, _update_date INTEGER NULL"
                    + ", _parent_id INTEGER NULL, FOREIGN KEY(_parent_id) REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT);";

   private static final String CARDS_TABLE = "CREATE TABLE __CARDS_TBL(_id INTEGER PRIMARY KEY AUTOINCREMENT,"
                    + "_value TEXT NOT NULL, _play_count INTEGER NULL,  "
                    + "_category_id INTEGER NOT NULL, FOREIGN KEY(_category_id) REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT);";

@Override
    public void onCreate(SQLiteDatabase db) {
        try {
            db.execSQL("PRAGMA foreign_keys = ON;");
            db.execSQL(CATEGORIES_TABLE);           
            db.execSQL(CARDS_TABLE);
            Logger.i("DB-INIT-DONE!");
        } catch (Exception ex) {
            Logger.e("Database on create error", ex);
        }

    }

ご覧のとおり、すべて問題ないようです。両方のテーブルに対して行を挿入、編集、選択できますが、残念ながら、子行を持つ行を削除できます。私の期待通りに。ON DELETE RESTRICT モードで 2 つのテーブル間に FK (外部キー) 関係を設定しているため、親テーブル (__CATEGORIES_TBL) から行を削除しようとすると例外が発生することが予想されますが、実際には親レコードが削除され、例外は発生しません。起こる、

理論上、sqlite は、__CARDS_TBL に 1 つ以上の子行がある場合、または __CATEGORIES_TBL に子行がある場合、__CATEGORIES_TBL の行を削除しないようにする必要がありますが、私のアプリケーションでは、親子関係の行がある場合に行を削除できます。

次のコードを検討してください(これは削除コードです)

private SQLiteDatabase  db;
public long delete(long objId) {
            try {
                            // TABLE_NAME can be __CATEGORIES_TBL or __CARDS_TBL  based on program flow
                return db.delete(TABLE_NAME, COLUMN_ROWID + "=" + objId, null);
            } catch (Exception e) {
                Logger.d("Unable to delete category <" + objId + ">.", e);
                return -123456;
            }
        }

db.delete を呼び出すたびに 1 が返されます (このコマンドによって 1 行が削除されることを意味します)。このコードは android 2.3 で実行されています。

前もって感謝します。

4

2 に答える 2

0

この問題について、私は db.execSQL("pragmaforeign_keys=on;"); を入れました。私のアプリコードの文を削除する前に、問題は解決しましたが、これは不要なコードだと思います。おそらく私の電話にインストールされているsqlite構成が間違っていて、再構成する必要があります。電話でsqliteを再構成することは可能ですか? テストデバイスはHTCワイルドファイアです(実行中のsqliteバージョンは3.7.2です)

于 2012-09-01T13:39:55.663 に答える
0

SQLite 3.7.9 に期待する動作が得られます。

sqlite> CREATE TABLE __CATEGORIES_TBL ( 
   ...>   _id INTEGER PRIMARY KEY AUTOINCREMENT, 
   ...>   _name TEXT NOT NULL, 
   ...>   _desc TEXT NULL,
   ...>   _create_date INTEGER NOT NULL, 
   ...>   _update_date INTEGER NULL, 
   ...>   _parent_id INTEGER NULL, 
   ...> FOREIGN KEY(_parent_id) 
   ...>   REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT
   ...> );
sqlite> 
sqlite> CREATE TABLE __CARDS_TBL(
   ...>   _id INTEGER PRIMARY KEY AUTOINCREMENT,
   ...>   _value TEXT NOT NULL, 
   ...>   _play_count INTEGER NULL,  
   ...>   _category_id INTEGER NOT NULL, 
   ...>   FOREIGN KEY(_category_id) 
   ...>     REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT
   ...> );
sqlite> 
sqlite> insert into __categories_tbl values 
   ...> (1, 'name', 'desc',current_date,current_date,1);
sqlite> insert into __cards_tbl values (1, 'value',3, 1);
sqlite> pragma foreign_keys=on;
sqlite> select * from __categories_tbl;
1|name|desc|2012-08-25|2012-08-25|1
sqlite> delete from __categories_tbl;
Error: foreign key constraint failed

私があなたなら、各ステップの後に SQLite データベースを視覚的に調べて、あなたが期待することが実際に起こっているかどうかを確認します。IIRC、すべての失敗で例外が発生するわけではありません。


外部キーのサポートは、バージョン 3.6.19 で導入されました。SQLite が外部キー制約を解析できるようにするコンパイル時の設定がありますが、実際にはそれらを強制することはできません。(SQLITE_OMIT_TRIGGER が定義されていますが、SQLITE_OMIT_FOREIGN_KEY は定義されていません。)トリガーを作成することで、SQLite のビルドが外部キーを強制できるかどうかを判断できるはずです。

トリガーの作成が解析エラーで失敗した場合、SQLite は外部キー ステートメントを解析しますが、それらを強制しません。3.6.19 のビルドを再コンパイルするか、新しいバージョンにアップグレードする必要があります。(そして、コンパイルする前にこれらの設定を確認してください。)

空の文字列を挿入すると、NOT NULL と宣言された列が空に見えることがあります。デフォルトでは、NULL と空の文字列は出力上で同一に見えます。列を選択すると、列にヌルが含まれているかどうかがわかります。

select * from table_name where column_name is null;
于 2012-08-25T15:06:24.487 に答える