Подтвердить что ты не робот

Как использовать запрос соединения в CursorLoader, когда его конструктор не поддерживает его

У меня есть две таблицы с соотношением 1: n, я использую контент-провайдер и загрузчик.

Как мне сделать запрос соединения для работы с загрузчиком курсора? Я мог бы как-то взломать его с помощью rawSql внутри поставщика контента, но как это сделать в конструкторе загрузчика курсора находится вне меня.

Спасибо большое!

CursorLoader (контекст контекста, Uri uri, String [] проекция, выбор строки, строка [] selectionArgs, String sortOrder)

Как будет запрашивать соединение, когда Uri может указывать только на одну таблицу

4b9b3361

Ответ 1

Uri не указывает на какую-либо таблицу. Он указывает на то, что вы хотите направить на него.

Предположим, что ваши две таблицы Customer и Order. У одного клиента может быть много заказов. Вы хотите выполнить запрос, чтобы получить все выдающиеся ордера... но вы хотите присоединиться к некоторым связанным с клиентами столбцам, которые вам понадобятся, например, имя клиента.

Предположим, что у вас уже есть content://your.authority.goes.here/customer и content://your.authority.goes.here/order, предназначенные для чисто запроса этих таблиц.

У вас есть два варианта:

  • Добавьте соединение отображаемого имени клиента на /order Uri. Наличие еще одного доступного столбца, вероятно, не нарушит существующих пользователей провайдера (хотя тестирование всегда является хорошей идеей). Это то, что делает ContactsContract - он объединяется в некоторые базовые столбцы, такие как имя контакта, почти во всех запросах всех таблиц.

  • Создайте content://your.authority.goes.here/orderWithCust, который выполняет тот же базовый запрос, что и /order, но содержит ваше соединение. В этом случае у вас могут быть insert(), update() и delete() выбросить какой-то RuntimeException, чтобы напомнить вам, что вы не должны изменять данные с помощью /orderWithCust как Uri.

В конце концов, разработка системы ContentProvider Uri аналогична разработке системы URL-адресов веб-службы REST. В обоих случаях соединение должно выполняться на стороне поставщика/сервера, поэтому вам может потребоваться разорвать базовую линию с одним столом на один, чтобы предложить некоторые полезные соединения.

Ответ 2

Я нашел решение, используя подклассы ContentProvider. Скажем, у вас есть таблица tblA и другая таблица tblB. Я рекомендую создать два класса "AContentProvider" и "BContentProvider". Самое главное, убедитесь, что обе таблицы настроены в одной базе данных.

Основная часть решения - переопределить ContentProvider.query() в ContentProvider, который вы выберете из CursorLoader - URI решает, какой из них:

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sort) {
    SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

    qb.setTables(
            "tblA LEFT JOIN tblB"
                    + " ON ("
                    + "tblA.b_id"
                    + " = "
                    + "tblB.id"
                    + ")"
    );

    ...
    // Content of projection is set by CursorLoader
    // usually in an Activity that implements LoaderManager.LoaderCallbacks<>

    Cursor c = qb.query(
            database,
            projection,
            selection,
            selectionArgs,
            groupBy,
            having,
            orderBy
    );

    ...
    return c;
}

Как вы можете видеть, JOIN выполняется в setTables(). Используя проекция, вы убедитесь, что вы показываете только нужные вам столбцы, и, самое главное, у вас нет повторяющихся столбцов, таких как "id" из обеих таблиц:

final String[] projection = new String[] {
        "tblA.*",
        "tblB.columnThatOnlyBHas"
};

Использовать переопределение и попытаться выполнить как можно большую работу в подклассах; например: все мои переопределенные методы query() вызывают setNotificationUri() для уведомления ContentResolver, если изменяется набор результатов курсора.:

c.setNotificationUri(getContext().getContentResolver(), uri);