Как реализовать сложные запросы с помощью Content Provider?

Я спрашиваю об этом, потому что я не совсем уверен, как работать с контентными провайдерами Android. У меня есть подмножество моей базы данных с 8 таблицами, и мне нужно создать сложные запросы, чтобы получить некоторые данные. Мой контент-провайдер отлично работает с простыми запросами. Например, у меня есть Person Person в моем классе PersonModel.java, и я получаю данные, используя:

String [] projection = {PersonModel.C_FIRST_NAME, PersonModel.C_LAST_NAME}; Cursor cursor = context.getContentResolver().query( MyProvider.CONTENT_URI_PERSONS, projection, null, null, null); 

И он отлично работает.

На MyProvider у меня есть константа CONTENT_URI, для каждой из моих таблиц.

 public class MyProvider extends ContentProvider { MyDbHelper dbHelper; SQLiteDatabase db; private static final String AUTHORITY = "com.myapp.models"; //Paths for each tables private static final String PATH_PROFILE_PICTURES = "profile_pictures"; private static final String PATH_PERSONS = "persons"; private static final String PATH_USERS = "users"; .... //Content URIs for each table public static final Uri CONTENT_URI_PROFILE_PICTURES = Uri .parse("content://" + AUTHORITY + "/" + PATH_PROFILE_PICTURES); public static final Uri CONTENT_URI_PERSONS = Uri.parse("content://" + AUTHORITY + "/" + PATH_PERSONS); public static final Uri CONTENT_URI_USERS = Uri.parse("content://" + AUTHORITY + "/" + PATH_USERS); ... private static final int PROFILE_PICTURES = 1; private static final int PROFILE_PICTURE_ID = 2; private static final int PERSONS = 3; private static final int PERSON_ID = 4; private static final int USERS = 5; private static final int USER_ID = 6; private static final UriMatcher sURIMatcher = new UriMatcher( UriMatcher.NO_MATCH); static { sURIMatcher.addURI(AUTHORITY, PATH_PROFILE_PICTURES, PROFILE_PICTURES); sURIMatcher.addURI(AUTHORITY, PATH_PROFILE_PICTURES + "/#", PROFILE_PICTURE_ID); sURIMatcher.addURI(AUTHORITY, PATH_PERSONS, PERSONS); sURIMatcher.addURI(AUTHORITY, PATH_PERSONS + "/#", PERSON_ID); sURIMatcher.addURI(AUTHORITY, PATH_USERS, USERS); sURIMatcher.addURI(AUTHORITY, PATH_USERS + "/#", USER_ID); ... } public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // Uisng SQLiteQueryBuilder instead of query() method SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); // check if the caller has requested a column which does not exists //checkColumns(projection); int uriType = sURIMatcher.match(uri); switch (uriType) { case PROFILE_PICTURES: queryBuilder.setTables(ProfilePictureModel.TABLE_PROFILE_PICTURE); break; case PROFILE_PICTURE_ID: // Adding the ID to the original query queryBuilder.appendWhere(ProfilePictureModel.C_ID + "=" + uri.getLastPathSegment()); case PERSONS: queryBuilder.setTables(PersonModel.TABLE_PERSON); break; case PERSON_ID: // Adding the ID to the original query queryBuilder.appendWhere(PersonModel.C_ID + "=" + uri.getLastPathSegment()); case USERS: queryBuilder.setTables(UserModel.TABLE_USER); break; case USER_ID: // Adding the ID to the original query queryBuilder.appendWhere(UserModel.C_ID + "=" + uri.getLastPathSegment()); default: throw new IllegalArgumentException("Unknown URI: " + uri); } db = dbHelper.getWritableDatabase(); Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); // make sure that potential listeners are getting notified cursor.setNotificationUri(getContext().getContentResolver(), uri); } 

Это небольшая часть моего поставщика контента. Поэтому мои вопросы:

1) Как реализовать rawQuery () в моем контент-провайдере? Или как я правильно использую свой запросBuilder?, Скажем, я хочу выполнить этот запрос, используя несколько таблиц, переименовав их, а также передав p1.id в качестве параметра?

 SELECT p1.first_name, p1_last_name FROM Person p1, Person P2, Relationship r WHERE p1.id = ? AND p1.id = r.relative_id AND p2.id = r.related_id; 

Я попытался сделать это, выполнив следующее: по моему методу query () (показано выше) у меня есть новый случай, называемый GET_RELATIVES:

 case GET_RELATIVES: db = dbHelper.getWritableDatabase(); queryBuilder.setTables(PersonModel.TABLE_PERSON + " p1, " + PersonModel.TABLE_PERSON + " p2, " + RelationshipModel.TABLE_RELATIONSHIP + " r"); queryBuilder.appendWhere("p2."+PersonModel.C_ID + "=" + uri.getLastPathSegment()); queryBuilder.appendWhere("p2."+PersonModel.C_ID + "=" + "r.related_id"); queryBuilder.appendWhere("p1."+PersonModel.C_ID + "=" + "r.relative_id"); 

Поэтому я определил новый PATH, CONTENT URI и добавлю его в UriMatcher, например:

 private static final String PATH_GET_RELATIVES = "get_relatives"; public static final Uri CONTENT_URI_GET_RELATIVES = Uri .parse("content://" + AUTHORITY + "/" + PATH_GET_RELATIVES); private static final int GET_RELATIVES = 22; private static final UriMatcher sURIMatcher = new UriMatcher( UriMatcher.NO_MATCH); static { ... sURIMatcher.addURI(AUTHORITY, PATH_GET_RELATIVES, GET_RELATIVES); } 

Но это, похоже, не работает, поэтому я думаю, что, вероятно, я определяю что-то не так на моем поставщике контента или внутри метода запроса.

2) Я не совсем уверен, в чем смысл иметь для каждой таблицы константу, называемую TABLE_ID, и добавление ее в случай переключения. Для чего это используется? Как мне это назвать?

Надеюсь, кто-нибудь может мне помочь, спасибо заранее!

На самом деле я нашел ответ на свой вопрос в самом очевидном месте: документация по Android.

Первый вопрос: выполнить rawQuery. Было ли это так:

Внутри моего коммутатора в провайдере контента я добавил новый URI, который для меня представляет собой JOIN между таблицами, поэтому я создал для него новую константу ContentUri, новый идентификатор и зарегистрировал его на UriMatcher, а затем написал rawQuery. Итак, MyProvider теперь выглядит так:

 public class MyProvider extends ContentProvider { ... // JOIN paths private static final String PATH_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES = "relationship_join_person_get_relatives"; ... public static final Uri CONTENT_URI_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES = Uri .parse("content://" + AUTHORITY + "/" + PATH_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES); ... private static final int RELATIONSHIP_JOIN_PERSON_GET_RELATIVES = 21; private static final UriMatcher sURIMatcher = new UriMatcher( UriMatcher.NO_MATCH); static { ... //JOINS sURIMatcher.addURI(AUTHORITY, PATH_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES + "/#", RELATIONSHIP_JOIN_PERSON_GET_RELATIVES); ... public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // Uisng SQLiteQueryBuilder instead of query() method SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); // check if the caller has requested a column which does not exists //checkColumns(projection); int uriType = sURIMatcher.match(uri); switch (uriType) { ... case RELATIONSHIP_JOIN_PERSON_GET_RELATIVES: db = dbHelper.getWritableDatabase(); String[] args = {String.valueOf(uri.getLastPathSegment())}; Cursor cursor = db.rawQuery( "SELECT p1.first_name, p1.last_name " + "FROM Person p1, Person p2, Relationship r " + "WHERE p1.id = r.relative_id AND " + "p2.id = r.related_id AND " + "p2.id = ?", args); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; ... } 

И чтобы вызвать метод query () и передать id ad параметр, я сделал это в своем контроллере:

 String[] projection = { PersonModel.C_FIRST_NAME, PersonModel.C_LAST_NAME }; Cursor cursor = context.getContentResolver().query( ContentUris.withAppendedId( AkdemiaProvider.CONTENT_URI_RELATIONSHIP_JOIN_PERSON_GET_RELATED, id), projection, null, null, null); 

Второй вопрос: наличие константы TABLE_ID полезно для запроса каждой таблицы, передающей идентификатор в качестве параметра, я не знал, как вызвать метод запроса, передающий такой идентификатор, и именно так в документации разработчика Android объясняется, как это сделать С использованием ContentUris.withAppendedId

 // Request a specific record. Cursor managedCursor = managedQuery( ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2), projection, // Which columns to return. null, // WHERE clause. null, // WHERE clause value substitution People.NAME + " ASC"); // Sort order. 

Я, ребята, хочу посмотреть всю документацию по этой ссылке.

Надеюсь, это поможет кому-то еще с той же проблемой понять ContentProvider, ContentUris и все такое 🙂

Ниже код работал для меня. Внутри поставщика контента вашего приложения:

 public static final String PATH_JOIN_TWO_TABLES = "my_path"; public static final Uri URI_JOIN_TWO_TABLES = Uri.parse("content://" + AUTHORITY + "/" + PATH_JOIN_TWO_TABLES); private static final int ID_JOIN_TWO_TABLES = 1001; private static final UriMatcher sURIMatcher = new UriMatcher( UriMatcher.NO_MATCH); static { sURIMatcher.addURI(AUTHORITY, PATH_JOIN_TWO_TABLES + "/#", ID_JOIN_TWO_TABLES ); } @Nullable @Override public Cursor query(@NonNull Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { int uriType = sURIMatcher.match(uri); switch (uriType) { case ID_JOIN_TWO_TABLES: return getWritableDatabase() .rawQuery("select * from " + "table_one" + " LEFT OUTER JOIN " + "table_two" + " ON (" + "table_one.ID" + " = " + "table_two.id" + ")", null); } return super.query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal); } 

И при создании запроса внутри вашей деятельности или фрагмента:

  Cursor cursor = getActivity().getContentResolver() .query(ContentUris.withAppendedId(MYContentProvider.URI_JOIN_TWO_TABLES, MyContentProvider.ID_JOIN_TWO_TABLES), null, null, null, null); 

Надеюсь, это сработает для вас.

Для простых запросов используйте selectionArgs в ContentProvider. Он работает, как показано ниже.

 String[] args = { "first string", "second@string.com" }; Cursor cursor = db.query("TABLE_NAME", null, "name=? AND email=?", args, null); 

Наличие TABLE_ID внутри, чтобы создать разные запросы для каждой таблицы.

Обратитесь к следующему классу для всех нескольких таблиц в контент-провайдерах

  1. Учебник Vogella 1
  2. Vogella Tutorial 2
  3. Рекомендации по экспорту нескольких таблиц с использованием поставщиков контента на Android