android - 实现android content provider的查询方法,使用多个表

标签 android android-sqlite android-contentprovider

我有三个表,想使用内容提供者来管理它们。下面是我的内容提供者的代码:

private static final UriMatcher sURIMatcher = new UriMatcher(
        UriMatcher.NO_MATCH);
static {
    sURIMatcher.addURI(AUTHORITY, METER_PATH, all_meters);
    sURIMatcher.addURI(AUTHORITY, METER_PATH + "/#", single_meter);

    sURIMatcher.addURI(AUTHORITY, CUSTOMERS_PATH, all_customers);
    sURIMatcher.addURI(AUTHORITY, CUSTOMERS_PATH + "/#", single_customer);

    sURIMatcher.addURI(AUTHORITY, BILL_PATH, all_bills);
    sURIMatcher.addURI(AUTHORITY, BILL_PATH + "/#", single_bill);

}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {

    SQLiteDatabase db = database.getWritableDatabase();

    // Using SQLiteQueryBuilder instead of query() method
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

queryBuilder
            .setTables(MeterTableDetails.TABLE_METERS
                    + " as meters "
                    + " INNER JOIN "
                    + CustomerTableDetails.TABLE_CUSTOMERS
                    + " as customers "
                    + " ON "
                    + (MeterTableDetails.METER_ID = CustomerTableDetails.KEY_METER_ID)
                    + " INNER JOIN "
                    + WaterBillTableDetails.TABLE_WATER_BILL
                    + " as waterbills "
                    + " ON "
                    + (CustomerTableDetails.KEY_METER_ID = WaterBillTableDetails.BILL_METER_ID));
    int uriType = sURIMatcher.match(uri);
    switch (uriType) {

    case all_meters:
        break;
    case single_meter:
        // Adding the ID to the original query

        String id = uri.getPathSegments().get(1);
        queryBuilder.appendWhere(MeterTableDetails.METER_ID + "=" + id);

        break;

    case all_customers:
        break;
    case single_customer:
        // Adding the ID to the original query
        String id1 = uri.getPathSegments().get(1);
        queryBuilder.appendWhere(CustomerTableDetails.KEY_CUSTOMER_ID + "="
                + id1);
        break;

    case all_bills:
        break;
    case single_bill:
        // Adding the ID to the original query
        String id2 = uri.getPathSegments().get(1);
        queryBuilder.appendWhere(WaterBillTableDetails.BILL_ID + "=" + id2);
        break;

    default:
        throw new IllegalArgumentException("Unknown URI: " + uri);
    }

    Cursor cursor = queryBuilder.query(db, projection, selection,
            selectionArgs, null, null, sortOrder);
    // Make sure that potential listeners are getting notified
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

    return cursor;
} 

我有三个表,并在 querybuilder.setTables 方法中创建了一些连接。我试图在 meters 表的列表中显示 meter 项目。我还有一个带有 loaderCallbacks 实现的 SimpleCursorAdapter。 目前我在我的 logcat 中收到以下错误,我认为这是因为连接和查询:

03-20 15:11:59.692: E/SQLiteCursor(2001): requesting column name with table name -- meters._id
03-20 15:11:59.692: E/SQLiteCursor(2001): java.lang.Exception
03-20 15:11:59.692: E/SQLiteCursor(2001):   at android.database.sqlite.SQLiteCursor.getColumnIndex(SQLiteCursor.java:180)
03-20 15:11:59.692: E/SQLiteCursor(2001):   at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:301)
03-20 15:11:59.692: E/SQLiteCursor(2001):   at android.database.CursorWrapper.getColumnIndexOrThrow(CursorWrapper.java:78)
03-20 15:11:59.692: E/SQLiteCursor(2001):   at android.support.v4.widget.SimpleCursorAdapter.findColumns(SimpleCursorAdapter.java:317)
03-20 15:11:59.692: E/SQLiteCursor(2001):   at android.support.v4.widget.SimpleCursorAdapter.swapCursor(SimpleCursorAdapter.java:328)
03-20 15:11:59.692: E/SQLiteCursor(2001):   at com.isys.waterbillingsystem.MetersActivity.onLoadFinished(MetersActivity.java:180)
03-20 15:11:59.692: E/SQLiteCursor(2001):   at com.isys.waterbillingsystem.MetersActivity.onLoadFinished(MetersActivity.java:1)

编辑

private static final String CREATE_CUSTOMER_VIEW = ""
        + "CREATE VIEW " + TABLE_CUSTOMER_VIEW 
        + " AS SELECT "+MeterTableDetails.TABLE_METERS+"."+MeterTableDetails.METER_ID+" AS "+ MeterTableDetails.TABLE_METERS+"."+MeterTableDetails.METER_ID +","+
        " "+CustomerTableDetails.KEY_FIRST_NAME+","+
        " "+CustomerTableDetails.KEY_LAST_NAME+","+
        " "+CustomerTableDetails.KEY_METER_ID+","+
        " "+CustomerTableDetails.KEY_METER_NUMBER+","+
        " "+CustomerTableDetails.KEY_PLOT_NUMBER+","+
        " "+CustomerTableDetails.TABLE_CUSTOMERS+"."+ CustomerTableDetails.KEY_CUSTOMER_ID+
        " FROM "+CustomerTableDetails.TABLE_CUSTOMERS+" AS customers "+" INNER JOIN "+MeterTableDetails.TABLE_METERS+" AS meters"+
        " ON "+CustomerTableDetails.KEY_METER_ID+" = "+MeterTableDetails.TABLE_METERS+"."+MeterTableDetails.METER_ID;


 public static TableDescriptor getDescriptor() {
    TableDescriptor descriptor = new TableDescriptor();
    descriptor.setTableName(TABLE_CUSTOMER_VIEW);
    descriptor.setColumnId(CUSTOMER_VIEW_ID);
    String[] available = { ViewCustomers.CUSTOMER_VIEW_ID,
            ViewCustomers.CUSTOMER_VIEW_LASTNAME,
            ViewCustomers.CUSTOMER_VIEW_LASTNAME,
            ViewCustomers.CUSTOMER_VIEW_KEY_METER_ID,
            ViewCustomers.CUSTOMER_VIEW_METER,
            ViewCustomers.CUSTOMER_VIEW_PLOT};

    descriptor.setAvailableColumns(available);
    return descriptor;
    }    

编辑 2

private static final String CREATE_METER_READING_VIEW = ""
        + "CREATE VIEW " + TABLE_METER_READING_VIEW
        + " AS SELECT " + WaterBillTableDetails.TABLE_WATER_BILL+ ".*"
        + ", " +CustomerTableDetails.TABLE_CUSTOMERS+"."+CustomerTableDetails.KEY_METER_NUMBER+","
        +" "+CustomerTableDetails.TABLE_CUSTOMERS+"."+CustomerTableDetails.KEY_PLOT_NUMBER+","
        +" "+CustomerTableDetails.TABLE_CUSTOMERS+"."+CustomerTableDetails.KEY_ACCOUNT_NUMBER+","
         +" "+CustomerTableDetails.TABLE_CUSTOMERS+"."+CustomerTableDetails.KEY_METER_ID+""
        +" FROM "+WaterBillTableDetails.TABLE_WATER_BILL+" AS waterbills "+" JOIN "+CustomerTableDetails.TABLE_CUSTOMERS+" AS customers"
        +" ON "+WaterBillTableDetails.BILL_CUSTOMER_ID+" ="+CustomerTableDetails.TABLE_CUSTOMERS+"."+CustomerTableDetails.KEY_CUSTOMER_ID;

Logcat 错误

03-25 10:45:03.476: E/AndroidRuntime(1144): FATAL EXCEPTION: main
03-25 10:45:03.476: E/AndroidRuntime(1144): java.lang.RuntimeException: Unable to start activity  ComponentInfo{com.isys.waterbillingsystem/com.isys.waterbillingsystem.CustomerDetailsAccountsActivity}:   java.lang.NullPointerException
03-25 10:45:03.476: E/AndroidRuntime(1144): Caused by: java.lang.NullPointerException
03-25 10:45:03.476: E/AndroidRuntime(1144):     at  com.isys.waterbillingsystem.CustomerDetailsAccountsActivity.onCreate(CustomerDetailsAccountsActivity.java:48 )

最佳答案

这是 View 的一个简单示例:

public class HektorDatabaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "hektor.db";
    private static final int DATABASE_VERSION = 91;

    public HektorDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // Method is called during creation of the database
    @Override
    public void onCreate(SQLiteDatabase database) {
        AppointmentTypesTable.onCreate(database);
    }

    // Method is called during an upgrade of the database,
    // e.g. if you increase the database version
    @Override
    public void onUpgrade(SQLiteDatabase database, int oldVersion,
            int newVersion) {
        AppointmentTypesTable.onUpgrade(database, oldVersion, newVersion);
    }


}

这是一个标准的 SQLiteOpenHelper。

    public class AppointmentWithTypeAndContactsView {

    public static final String TABLE_NAME = "appointments_with_type_and_contacts";

    public static final String COLUMN_ID = AppointmentsTable.COLUMN_ID;
    public static final String COLUMN_EXTERNAL_ID = AppointmentsTable.COLUMN_EXTERNAL_ID;
    public static final String COLUMN_START_DATE = AppointmentsTable.COLUMN_START_DATE;

    private static final String DATABASE_CREATE = ""
            + "CREATE VIEW " + TABLE_NAME 
            + " AS SELECT " + AppointmentsTable.TABLE_NAME + ".*"
            + ", " + AppointmentTypesTable.TABLE_NAME + "." + AppointmentTypesTable.COLUMN_NAME
            + ", " + BuyersTable.TABLE_NAME + "." + BuyersTable.COLUMN_TITLE + " || ' ' || " + BuyersTable.TABLE_NAME + "." + BuyersTable.COLUMN_LAST_NAME + " || ' ' || " + BuyersTable.TABLE_NAME + "." + BuyersTable.COLUMN_FIRST_NAME + " AS " + BuyersTable.COLUMN_LAST_NAME
            + ", " + SellersDetailsTable.TABLE_NAME + "." + SellersDetailsTable.COLUMN_TITLE + " || ' ' || " + SellersDetailsTable.TABLE_NAME + "." + SellersDetailsTable.COLUMN_LAST_NAME + " || ' ' || " + SellersDetailsTable.TABLE_NAME + "." + SellersDetailsTable.COLUMN_FIRST_NAME + " AS " + SellersDetailsTable.COLUMN_LAST_NAME
            + " FROM " + AppointmentsTable.TABLE_NAME + " LEFT OUTER JOIN " + AppointmentTypesTable.TABLE_NAME
            + " ON " + AppointmentsTable.TABLE_NAME + "." + AppointmentsTable.COLUMN_TYPE 
            + " = " + AppointmentTypesTable.TABLE_NAME + "." + AppointmentTypesTable.COLUMN_EXTERNAL_ID
            + " LEFT OUTER JOIN " + BuyersTable.TABLE_NAME
            + " ON " +  AppointmentsTable.TABLE_NAME + "." + AppointmentsTable.COLUMN_BUYER
            + " = " + BuyersTable.TABLE_NAME + "." + BuyersTable.COLUMN_EXTERNAL_ID
            + " LEFT OUTER JOIN " + SellersDetailsTable.TABLE_NAME
            + " ON " +  AppointmentsTable.TABLE_NAME + "." + AppointmentsTable.COLUMN_SELLER
            + " = " + SellersDetailsTable.TABLE_NAME + "." + SellersDetailsTable.COLUMN_EXTERNAL_ID;



    public static void onCreate(SQLiteDatabase database) {
        database.execSQL(DATABASE_CREATE);
    }

    public static void onUpgrade(SQLiteDatabase database, int oldVersion,
            int newVersion) {
        Log.w(AppointmentWithTypeAndContactsView.class.getName(), "Upgrading database from version "
                + oldVersion + " to " + newVersion
                + ", which will destroy all old data");
        database.execSQL("DROP VIEW IF EXISTS " + TABLE_NAME);
        onCreate(database);
    }

    public static TableDescriptor getDescriptor() {
        TableDescriptor descriptor = new TableDescriptor();
        descriptor.setTableName(TABLE_NAME);
        descriptor.setColumnId(COLUMN_ID);

        String[] appointmentsAvailableColumns = AppointmentsTable.getDescriptor().getAvailableColumns();
        String[] typesAvailableColumns = new String[] {AppointmentTypesTable.COLUMN_NAME};
        String[] buyersAvailableColumns = new String[] {BuyersTable.COLUMN_LAST_NAME};
        String[] sellerssAvailableColumns = new String[] {SellersDetailsTable.COLUMN_LAST_NAME};

        descriptor.setAvailableColumns(ArrayUtils.concatAll(appointmentsAvailableColumns, typesAvailableColumns, buyersAvailableColumns, sellerssAvailableColumns));
        return descriptor;
    }

}

我更新了 Table 类以包含一些实用方法。

public class TableDescriptor {
    private String tableName;
    private String columnId;
    private String[] availableColumns;

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getColumnId() {
        return columnId;
    }

    public void setColumnId(String columnId) {
        this.columnId = columnId;
    }

    public String[] getAvailableColumns() {
        return availableColumns;
    }

    public void setAvailableColumns(String[] availableColumns) {
        this.availableColumns = availableColumns;
    }
}

TableDescriptor 只是一个容器类。

public final class HektorContentProviderContract {
    public static final String AUTHORITY = "fr.intuitiv.hektor.contentprovider";

    public static final String APPOINTMENT_WITH_TYPE_BASE_PATH = "appointment_with_type";
    public static final Uri APPOINTMENT_WITH_TYPE_CONTENT_URI = Uri.parse("content://" + AUTHORITY
            + "/" + APPOINTMENT_WITH_TYPE_BASE_PATH);
    public static final String APPOINTMENT_WITH_TYPE_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
            + "/vnd." + AUTHORITY + "." + APPOINTMENT_WITH_TYPE_BASE_PATH;
    public static final String APPOINTMENT_WITH_TYPE_CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
            + "/vnd." + AUTHORITY + "." + APPOINTMENT_WITH_TYPE_BASE_PATH;
}

我通常创建一些“合约”类来存储任何公共(public)常量。

public class ContentProviderHelper {
    private Context context;

    public Context getContext() {
        return context;
    }

    public void setContext(Context context) {
        this.context = context;
    }

    public ContentProviderHelper(Context context) {
        this.setContext(context);
    }

    public Cursor query(SQLiteOpenHelper database, TableDescriptor table,
            boolean isSingular, Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {

        // Uisng SQLiteQueryBuilder instead of query() method
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

        // Set the table
        queryBuilder.setTables(table.getTableName());

        if (isSingular) {
            queryBuilder.appendWhere(table.getColumnId() + "="
                    + uri.getLastPathSegment());
        }

        SQLiteDatabase db = database.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);

        return cursor;
    }

    public Uri insert(SQLiteOpenHelper database, TableDescriptor table, Uri uri, ContentValues values) {
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        long id = 0;
        id = sqlDB.insertWithOnConflict(table.getTableName(), null, values, SQLiteDatabase.CONFLICT_REPLACE);
        getContext().getContentResolver().notifyChange(uri, null);
        return Uri.withAppendedPath(getTableUri(table), Long.toString(id));
    }

    public int delete(SQLiteOpenHelper database, TableDescriptor table, boolean isSingular, Uri uri, String selection, String[] selectionArgs) {
        int rowsDeleted = 0;
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        if (!isSingular) {
            rowsDeleted = sqlDB.delete(table.getTableName(), selection,
                    selectionArgs);
        } else {
            String id = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsDeleted = sqlDB.delete(table.getTableName(),
                        table.getColumnId() + "=" + id, null);
            } else {
                rowsDeleted = sqlDB.delete(table.getTableName(),
                        table.getColumnId() + "=" + id + " and " + selection,
                        selectionArgs);
            }
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsDeleted;
    }

    public int update(SQLiteOpenHelper database, TableDescriptor table, boolean isSingular, Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {

        SQLiteDatabase sqlDB = database.getWritableDatabase();
        int rowsUpdated = 0;
        if (!isSingular) {
            rowsUpdated = sqlDB.update(table.getTableName(), values, selection,
                    selectionArgs);
        } else {
            String id = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsUpdated = sqlDB.update(table.getTableName(), values,
                        table.getColumnId() + "=" + id, null);
            } else {
                rowsUpdated = sqlDB.update(table.getTableName(), values,
                        table.getColumnId() + "=" + id + " and " + selection,
                        selectionArgs);
            }
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsUpdated;
    }

    public void checkColumns(TableDescriptor table, String[] projection) {
        String[] available = table.getAvailableColumns();
        if (projection != null) {
            HashSet<String> requestedColumns = new HashSet<String>(
                    Arrays.asList(projection));
            HashSet<String> availableColumns = new HashSet<String>(
                    Arrays.asList(available));
            // Check if all columns which are requested are available
            if (!availableColumns.containsAll(requestedColumns)) {
                throw new IllegalArgumentException(
                        "Unknown columns in projection");
            }
        }
    }

    protected Uri getTableUri(TableDescriptor table) {
        Uri result = null;

        String tableName = table.getTableName();
        if (AppointmentWithTypeView.TABLE_NAME.equals(tableName)) {
            result = HektorContentProviderContract.APPOINTMENT_WITH_TYPE_CONTENT_URI;
        }

        return result;
    }
}

这是我在使用 ContentProvider 时使用的另一个实用程序类。它简化了数据库管理操作。如果您要管理很多表,这将非常方便。

public class UriDescriptor {
    private TableDescriptor table;
    private boolean singular;

    public boolean isSingular() {
        return singular;
    }

    public void setSingular(boolean singular) {
        this.singular = singular;
    }

    public TableDescriptor getTable() {
        return table;
    }

    public void setTable(TableDescriptor table) {
        this.table = table;
    }
}

这又只是一个容器类——不是很有趣。

public class HektorContentProvider extends ContentProvider {

    private ContentProviderHelper helper;

    // database
    private HektorDatabaseHelper database;

    // Used for the UriMacher
    private static final int APPOINTMENT_WITH_TYPE = 290;
    private static final int APPOINTMENT_WITH_TYPE_ID = 300;

    private static final UriMatcher sURIMatcher = new UriMatcher(
            UriMatcher.NO_MATCH);
    static {
        sURIMatcher.addURI(HektorContentProviderContract.AUTHORITY,
                HektorContentProviderContract.APPOINTMENT_WITH_TYPE_BASE_PATH, APPOINTMENT_WITH_TYPE);
        sURIMatcher.addURI(HektorContentProviderContract.AUTHORITY,
                HektorContentProviderContract.APPOINTMENT_WITH_TYPE_BASE_PATH + "/#",
                APPOINTMENT_WITH_TYPE_ID);
    }

    @Override
    public boolean onCreate() {
        database = new HektorDatabaseHelper(getContext());
        helper = new ContentProviderHelper(getContext());
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {

        UriDescriptor descriptor = getDescriptor(uri);
        helper.checkColumns(descriptor.getTable(), projection);

        Cursor cursor = helper.query(database, descriptor.getTable(),
                descriptor.isSingular(), uri, projection, selection,
                selectionArgs, sortOrder);

        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        UriDescriptor descriptor = getDescriptor(uri);
        Uri result = helper
                .insert(database, descriptor.getTable(), uri, values);
        return result;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int rowsDeleted = 0;
        UriDescriptor descriptor = getDescriptor(uri);
        rowsDeleted = helper.delete(database, descriptor.getTable(),
                descriptor.isSingular(), uri, selection, selectionArgs);
        return rowsDeleted;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {

        UriDescriptor descriptor = getDescriptor(uri);
        int rowsUpdated = helper.update(database, descriptor.getTable(),
                descriptor.isSingular(), uri, values, selection, selectionArgs);
        return rowsUpdated;
    }

    protected UriDescriptor getDescriptor(Uri uri) {
        UriDescriptor descriptor = new UriDescriptor();

        int uriType = sURIMatcher.match(uri);
        switch (uriType) {
        case APPOINTMENT_WITH_TYPE:
            descriptor.setSingular(false);
            descriptor.setTable(AppointmentWithTypeView.getDescriptor());
            break;
        case APPOINTMENT_WITH_TYPE_ID:
            descriptor.setSingular(true);
            descriptor.setTable(AppointmentWithTypeView.getDescriptor());
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
        }

        return descriptor;
    }

}

那是 ContentProvider 类。它非常简单,因为大部分工作都是在 ContentProviderHelper 类中完成的。

这是一个表示 View 的类。我为我想在我的数据库中创建的每个 View 或表编写这样的类。这两个实体的使用方式几乎相同——您只需将 SQL 语句从 CREATE VIEW 更改为 CREATE TABLE。如您所见,可以基于 SELECT 语句创建 View - 因此如果您想连接多个表,它们将非常有用。当插入任何 AppointmentsTable/AppointmentTypesTable 表时,数据也可以通过 View 获得。所以我更喜欢创建这样的 View 和一个 ContentProvider 来处理它。 SELECT 查询很简单(只需从 View 中读取)。不过,您必须以不同方式处理 INSERT/DELETES - 即,将数据插入关联表。

我的应用程序从网络服务加载数据,所以我在后台进行。我在 ContentProvider 中为每个表(即 AppointmentsTableAppointmentTypesTable)创建了 CONTENT_URL。这些被后台进程用来插入/更新数据。 UI 仅使用连接到 View 的 CONTENT_URL,因为它们只需要读取数据。

如果您有想法,请告诉我。如果需要,我可以分享更多代码。 :)

关于android - 实现android content provider的查询方法,使用多个表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22554684/

相关文章:

java - 如何通过另一个 Activity 显示数据库中的特定行?

android - 为什么/我们应该在 Android 中使用 Content Provider 时实现 BaseColumns?

android - 用于在未启动的android和服务器之间同步数据的SyncService

android - 如何使用 Google App 签名为 Sms Retriever 生成 11 个字符的哈希 key

android - 使用 Google Maps API v2 时,已签名的 APK 会崩溃,未签名的则不会

java - 在Android应用程序中分解字符串

android - ExoPlayer shuffle 不会重现所有歌曲

android - 如何将 SQLite 数据库中的标记输出到 MapView 和 Google Map

java - 如何从 Sqlite 数据库的多列中搜索字符串并在 ListView 中返回结果?

android - 共享内容提供者