代码之家  ›  专栏  ›  技术社区  ›  Bostone

Android:sqlite一对多设计

  •  11
  • Bostone  · 技术社区  · 14 年前

    对于如何实现一对多映射,任何人都有很好的建议 SQLite 使用 ContentProvider 是吗?如果你看 Uri ContentProvider#insert(Uri, ContentValues) 你可以看到它有 ContentValues 包含要插入的数据的参数。问题是,在当前的实现中 内容价值 不支持 put(String, Object) 方法和类是最终的,所以我不能扩展它。为什么会有问题?以下是我的设计:

    我有两张表,它们在一对多关系中。为了在代码中表示这些,我有两个模型对象。1st表示主记录,它有一个字段,该字段是第二个对象实例的列表。现在,我在模型对象1中有一个helper方法,它返回 内容值 从当前对象生成。用 ContentValues#put 重载方法,但我不太幸运。因此,由于我的第二个表行只是一个字符串值,所以我生成了一个逗号分隔的字符串,然后在内部重新分析为字符串[] 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插入 方法

    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();
        }
    
    }
    
    3 回复  |  直到 13 年前
        1
  •  6
  •   CommonsWare    14 年前

    我认为你看的是一对多关系的错误结局。

    看看 ContactsContract 例如,内容提供者。联系人可以有多个电子邮件地址、多个电话号码等。通过在 “多” 一边。若要添加新的电话号码,请插入新的电话号码,提供电话号码所属联系人的ID。

    如果您有一个没有内容提供程序的普通sqlite数据库,也可以这样做。关系数据库中的一对多关系是通过在“多”端的表上插入/更新/删除来实现的,每个表都有一个返回“一”端的外键。

    现在,从OO的角度来看,这并不理想。欢迎您创建ORM样式的包装器对象(想想Hibernate),它允许您从“一方”操作子集合。然后,一个足够智能的集合类可以返回并同步“many”表以进行匹配。然而,要正确地实现这些并不一定是微不足道的。

        2
  •  5
  •   Community Paul Sweatte    7 年前

    你可以使用 ContentProviderOperations 为了这个。

    它们基本上是批量操作,能够返回对为父行生成的标识符的引用。

    怎么用? 内容提供属性 可用于一对多的设计,在这个答案中有很好的解释: What are the semantics of withValueBackReference?

        3
  •  4
  •   Bostone    14 年前

    所以我要回答我自己的问题。我有两张桌子和两个模型对象,走上了正确的道路。所缺少的和使我困惑的是,我想直接通过 ContentProvider#insert 一次通话。这是错误的。 ContentProvider 应该创建和维护这两个表,但是决定使用哪个表应该由的uri参数决定。 ContentProvider插入 . 使用ContentResolver和向模型对象添加“addfoo”等方法非常方便。这样的方法将采用ContentResolver参数,最后是插入复杂记录的顺序:

    1. 通过插入父记录 ContentProvider插入 并获取记录ID
    2. 为每个子项提供父ID(外键)并使用 ContentProvider插入 使用不同的URI插入子记录

    所以剩下的唯一问题是如何在事务中封装上述代码?