11

SQLite誰もが使用するための1対多のマッピングを実装する方法について良いアドバイスを持っていContentProviderますか?見てみると、挿入するデータを含むパラメータが含まれUri ContentProvider#insert(Uri, ContentValues)ていることがわかります。ContentValues問題は、現在の実装ではメソッドをContentValuesサポートしておらずput(String, Object)、クラスがfinalであるため、拡張できないことです。なぜそれが問題なのですか?これが私のデザインです:

1対多の関係にある2つのテーブルがあります。これらをコードで表すために、2つのモデルオブジェクトがあります。1番目はメインレコードを表し、2番目のオブジェクトインスタンスのリストであるフィールドがあります。ContentValuesこれで、現在のオブジェクトから生成されたものを返すヘルパーメソッドがモデルオブジェクト#1にあります。プリミティブフィールドにContentValues#putオーバーロードされたメソッドを入力するのは簡単ですが、リストがうまくいきません。したがって、現在、2番目のテーブル行は単一の文字列値であるため、コンマで区切られた文字列を生成し、それを内部のString[]に再解析しContentProvider#insertます。それは不愉快に感じるので、誰かがそれをよりクリーンな方法で行う方法をほのめかすことができるかもしれません。

ここにいくつかのコードがあります。モデルクラスからの最初:

public ContentValues toContentValues() {
    ContentValues values = new ContentValues();
    values.put(ITEM_ID, itemId);
    values.put(NAME, name);
    values.put(TYPES, concat(types));
    return values;
}

private String concat(String[] values) { /* trivial */}

ContentProvider#insertこれがメソッドのスリム化バージョンです

public Uri insert(Uri uri, ContentValues values) {
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        // populate types
        String[] types = ((String)values.get(Offer.TYPES)).split("|");
        // we no longer need it
        values.remove(Offer.TYPES);
        // first insert row into OFFERS
        final long rowId = db.insert("offers", Offer.NAME, values);
        if (rowId > 0 && types != null) {
            // now insert all types for the row
            for (String t : types) {
                ContentValues type = new ContentValues(8);
                type.put(Offer.OFFER_ID, rowId);
                type.put(Offer.TYPE, t);
                // insert values into second table
                db.insert("types", Offer.TYPE, type);
            }
        }
        db.setTransactionSuccessful();
        return ContentUris.withAppendedId(Offer.CONTENT_URI, rowId);
    } catch (Exception e) {
        Log.e(TAG, "Failed to insert record", e);
    } finally {
        db.endTransaction();
    }

}
4

3 に答える 3

6

あなたは1対多の関係の間違った終わりを見ていると思います。

ContactsContractたとえば、コンテンツプロバイダーを見てください。連絡先には、多くの電子メールアドレス、多くの電話番号などを含めることができます。これを実現する方法は、「多くの」側で挿入/更新/削除を行うことです。新しい電話番号を追加するには、新しい電話番号を挿入し、その電話番号が関係する連絡先のIDを指定します。

コンテンツプロバイダーのないプレーンなSQLiteデータベースがある場合も同じことを行います。リレーショナルデータベースでの1対多の関係は、「多」側のテーブルでの挿入/更新/削除によって実現されます。各テーブルには、「1」側に戻る外部キーがあります。

さて、OOの観点からは、これは理想的ではありません。「1つの」側から子のコレクションを操作できるようにするORMスタイルのラッパーオブジェクト(Hibernateを考えてください)を作成することを歓迎します。次に、十分にインテリジェントなコレクションクラスを使用して、「多くの」テーブルを同期させて一致させることができます。ただし、これらを適切に実装することは必ずしも簡単ではありません。

于 2010-02-05T13:31:09.783 に答える
5

これに使用できますContentProviderOperations

これらは基本的に、親行用に生成された識別子を逆参照する機能を備えた一括操作です。

1対多の設計にどのContentProviderOperationsように使用できるかは、この回答で非常によく説明されています。withValueBackReferenceのセマンティクスは何ですか?

于 2011-12-01T20:04:36.660 に答える
4

だから私は自分の質問に答えるつもりです。私は2つのテーブルと2つのモデルオブジェクトを持って正しい方向に進んでいました。ContentProvider#insert足りないものと私を混乱させたのは、1回の呼び出しで複雑なデータを直接挿入したかったことです。これは間違っています。ContentProviderこれらの2つのテーブルを作成して維持する必要がありますが、使用するテーブルの決定は、のURIパラメータによって決定される必要がありますContentProvider#insert。ContentResolverを使用して、モデルオブジェクトに「addFoo」などのメソッドを追加すると非常に便利です。このようなメソッドはContentResolverパラメーターを取り、最後に複雑なレコードを挿入するシーケンスを示します。

  1. 親レコードを挿入しContentProvider#insert、レコードIDを取得します
  2. 子ごとに親ID(外国のキー)を提供ContentProvider#insertし、異なるURIで使用して子レコードを挿入します

では、残っている唯一の質問は、上記のコードをトランザクションでどのようにエンベロープするかです。

于 2010-02-05T23:49:42.387 に答える