我有两个表1:n的关系,我使用内容提供程序和游标加载器。
如何使连接查询与游标加载器一起工作?我可以用rawSql在内容提供程序中破解它,但如何在游标加载器构造函数中做到这一点超出了我的能力。
谢谢你!
CursorLoader(Context Context, Uri Uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
当Uri只能指向一个表时,如何查询join
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
系统类似于设计一个REST Web服务的URL系统。在这两种情况下,连接都必须在提供者/服务器端完成,因此您可能需要打破一个表到一个url的基线来提供一些有用的连接。
我找到了一个使用ContentProvider子类的解决方案。假设有表格tblA和另一个表格tblB。我建议创建两个类"AContentProvider"one_answers"BContentProvider"。最重要的是,确保这两个表都设置在同一个数据库中。
解决方案的主要部分是覆盖ContentProvider中的ContentProvider.query(),你将从你的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);