Вопрос

У кого-нибудь есть хороший совет о том, как реализовать картирование с одним ко многим SQLite с использованием ContentProvider? Если вы посмотрите на Uri ContentProvider#insert(Uri, ContentValues) Вы можете видеть, что это ContentValues парамет, который содержит данные для вставки. Проблема в том, что в своей текущей реализации ContentValues не поддерживается put(String, Object) Метод и класс являются окончательными, поэтому я не могу его расширить. Почему это проблема? Вот мой дизайн:

У меня есть 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 Поставщик контента, например. Контакты могут иметь много адресов электронной почты, много телефонов и т. Д. "много" сторона. Чтобы добавить новый номер телефона, вы вставляете новый номер телефона, предоставляя идентификатор контакта, для которого относится номер телефона.

Вы бы сделали то же самое, если бы у вас была простая база данных SQLite без поставщика контента. Отношения с одним ко многим в реляционных базах данных достигаются посредством вставки/обновлений/удалений на таблице для «многих» стороной, каждый из которых имеет внешний ключ обратно на «единую» сторону.

Теперь, с точки зрения OO, это не идеально. Вы можете создать объекты обертки в стиле ORM (подумайте о спячке), которые позволяют вам манипулировать коллекцией детей с «одной» стороны. Достаточно разветвленный класс сбора может затем обойти и синхронизировать таблицу «много», чтобы соответствовать. Однако они не обязательно тривиальны для правильного реализации.

Другие советы

Вы можете использовать ContentProviderOperations для этого.

Они в основном представляют собой объемные операции с возможностью обратной ссылки на идентификаторы, генерируемые для родительских строк.

Как ContentProviderOperations может быть использован для дизайна от одного ко многим, очень хорошо объяснен в этом ответе: Какова семантика в счете

Так что я собираюсь ответить на свой вопрос. Я был на правильном пути с двумя таблицами и двумя модельными объектами. Чего не хватало и что меня смутило, так это то, что я хотел напрямую вставить сложные данные через ContentProvider#insert в одном вызове. Это не правильно. ContentProvider должен создать и поддерживать эти две таблицы, но решение о том, какая таблица использовать, должно быть продиктовано параметром URI ContentProvider#insert. Анкет Очень удобно использовать ContentResolver и добавлять такие методы, как «AddFoo» в объект модели. Такой метод будет принимать параметр ContentReSolver, и в конце вот последовательность для вставки сложной записи:

  1. Вставить родительский рекорд через ContentProvider#insert и получить идентификатор записи
  2. В каждом ребенке предоставляют родительский идентификатор (Foregn Key) и используйте ContentProvider#insert с разным URI для вставки детских записей

Итак, единственный оставшийся вопрос - как охватить приведенный выше код в транзакции?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top