Android:Sqlite 1対Manyデザイン
-
18-09-2019 - |
質問
誰もが1対多マッピングを実装する方法について良いアドバイスを持っています SQLite
使用 ContentProvider
?あなたが見たら Uri ContentProvider#insert(Uri, ContentValues)
あなたはそれが持っていることを見ることができます ContentValues
挿入するデータを含むPARAM。問題は、現在の実装においてです ContentValues
サポートしていません put(String, Object)
方法とクラスが最終的なので、拡張できません。なぜそれが問題なのですか?ここに私のデザインがあります:
1対多数の関係にある2つのテーブルがあります。これらをコードで表すには、2つのモデルオブジェクトがあります。 1番目はメインレコードを表し、2番目のオブジェクトインスタンスのリストであるフィールドがあります。これで、モデルオブジェクト#1にヘルパーメソッドがあります。 ContentValues
現在のオブジェクトから生成されました。プリミティブフィールドを入力するのは些細なことです ContentValues#put
過負荷の方法ですが、私はリストのために運が悪いです。だから現在、私の2番目のテーブル行が単一の文字列値であるため、私はコンマの区切り文字列を生成します。 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();
}
}
解決
私はあなたが一つの関係の間違った終わりを見ていると思います。
を見てください ContactsContract
たとえば、コンテンツプロバイダー。連絡先は、多くのメールアドレス、多くの電話番号などを持つことができます。 "たくさんの" 側。新しい電話番号を追加するには、新しい電話番号を挿入して、電話番号が関係する連絡先のIDを提供します。
コンテンツプロバイダーのないプレーンSQLiteデータベースがある場合、同じことをします。リレーショナルデータベースの1対多くの関係は、「多くの」側のテーブルの挿入/更新/削除を介して達成され、それぞれが「1つの」側に外部キーを持っています。
今、OOの観点から、これは理想的ではありません。 「1つの」側から子供のコレクションを操作できるORMスタイルのラッパーオブジェクト(Hibernateを考えてください)を作成できます。その後、十分に知性のあるコレクションクラスが向きを変えて、「多くの」テーブルを同期して一致させることができます。ただし、これらは必ずしも適切に実装する些細なものではありません。
他のヒント
使用できます ContentProviderOperations
このため。
それらは基本的に、親の行に生成された識別子に戻ることができる能力を備えたバルク操作です。
どのように ContentProviderOperations
1対多様なデザインに使用できます。この答えでは非常によく説明されています。 withvaluebackreferenceのセマンティクスは何ですか?
だから私は自分の質問に答えるつもりです。私は2つのテーブルと2つのモデルオブジェクトを持っていることで正しい軌道に乗っていました。不足していて、私を混乱させたのは、複雑なデータを直接挿入したいということでした ContentProvider#insert
1回の呼び出しで。これは間違っています。 ContentProvider
これらの2つのテーブルを作成および維持する必要がありますが、どのテーブルを使用するかを決定する決定は、 ContentProvider#insert
. 。 ContentResolverを使用し、モデルオブジェクトに「AddFoo」などのメソッドを追加するのは非常に便利です。このような方法はContentResolverパラメーターを取り、最後に複雑なレコードを挿入するシーケンスがあります。
- 親の記録を挿入します
ContentProvider#insert
記録IDを取得します - 各子供ごとに親ID(foregn key)を提供し、使用します
ContentProvider#insert
異なるURIを使用して、子の記録を挿入します
したがって、残っている唯一の質問は、上記のコードをトランザクションに封入する方法です。