Как использовать запрос соединения в CursorLoader, когда его конструктор не поддерживает его
У меня есть две таблицы с соотношением 1: n, я использую контент-провайдер и загрузчик.
Как мне сделать запрос соединения для работы с загрузчиком курсора? Я мог бы как-то взломать его с помощью rawSql внутри поставщика контента, но как это сделать в конструкторе загрузчика курсора находится вне меня.
Спасибо большое!
CursorLoader (контекст контекста, Uri uri, String [] проекция, выбор строки, строка [] selectionArgs, String sortOrder)
Как будет запрашивать соединение, когда Uri может указывать только на одну таблицу
Ответы
Ответ 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);