【问题标题】:implement the android content provider's query method,with multiple tables in use实现android content provider的查询方法,使用多个表
【发布时间】:2014-03-21 09:21:00
【问题描述】:

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

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 方法中创建了一些连接。我正在尝试在仪表表的列表中显示仪表项目。我还有一个带有 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 )

【问题讨论】:

  • 我将在今天晚些时候尝试重现您的代码,以查看导致错误的原因。同时-您是否尝试过使用视图?我发现它们非常有用,因为在处理单个表时,许多 Android 的数据库管理更容易使用。
  • 非常感谢@SamuilYanovski !..这件事真的困扰了我好几天..我已经阅读了这些观点......但真的不知道如何实现它们。做你介意向我展示不同的场景是如何工作的吗?内容提供者和视图,以便我看到不同之处。

标签: android android-sqlite android-contentprovider


【解决方案1】:

下面是视图的一个简单示例:

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;
}

我通常会创建一些“Contract”类来存储任何公共常量。

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;
    }
}

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

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 类中完成的。

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

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

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

【讨论】:

  • 哇,这太棒了..所以据我所知,我可以有一个数据库助手类,其中包含每个视图所需的多个类?那么内容提供者类会是什么?看起来像?以我的例子为例,我有 3 个表。这在查询中是什么意思 '.*'。我从未见过它们一起使用
  • 是的,这或多或少是个想法——你有一个辅助类和多个表/视图类(即每个表/视图都有一个这样的文件)。 table_name.* 表示every_column_from_that_table。单独编写每一列会更短。 :) SimpleCursorAdapter 与 View / Table 的工作方式相同。您只需查询视图的 CONTENT_URL 并将光标提供给适配器。我将编辑答案以添加一些关于 ContentProvider 的代码 - 不过需要几个小时才能完成,因为我现在得走了。 :)
  • 好的..非常感谢您的帮助...我将等待反馈
  • 这是一个SQL错误导致的。您所有的表都有“_id”列。因此,您不能只说“SELECT VIEW table_a.*, table_b.*”,因为这将包括两个“_id”列,并且数据库无法确定您真正想要的列(这就是 _id 模棱两可的原因 -有多个具有此名称的列)。检查我的示例 - 我仅包含主表中的所有列 (AppointmentTypesTable)。从其他表中,我只取特定列(我没有包括买方/卖方表中的 _id 列)。我想类似的东西也适合你。 :)
  • 所以你有:账单是指客户,而客户是指仪表(即 bill.customer_id = customer.id 和 customer.meter_id = Meter.id)。它是否正确?如果是这种情况 - 完全没问题,没有什么可以改进的。 :) 根据 Android 指南,您应该在每个表中都有 _id 列 - 不用担心。只需让您在 SELECT 语句中重命名这些列(使用“AS”关键字)或将它们从语句中删除(您可以取而代之的是外键列,因为根据您的 WHERE 子句它们是相等的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-10
  • 2017-05-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多