我有三个表,并希望使用内容提供商来管理它们。下面是我的内容提供商提供的代码:
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 )
答案 0 :(得分:5)
以下是Views的快速示例:
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 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
类中完成的。
这是一个代表View的类。我为每个要在我的数据库中创建的视图或表编写类。两个实体几乎都以相同的方式使用 - 您只需将SQL语句从CREATE VIEW更改为CREATE TABLE。如您所见,可以基于SELECT语句创建视图 - 因此,如果要连接多个表,它们非常有用。当插入任何AppointmentsTable
/ AppointmentTypesTable
表时,数据也可以通过View获得。所以我更喜欢创建这样的View和ContentProvider
来处理它。 SELECT查询很简单(只需从View中读取)。您必须以不同的方式处理INSERT / DELETES - 即将数据插入关联的表。
我的应用程序从Web服务加载其数据,所以我在后台进行。我在ContentProvider中为每个表创建了CONTENT_URL(即AppointmentsTable
和AppointmentTypesTable
)。后台进程使用这些来插入/更新数据。 UI仅使用连接到视图的CONTENT_URL,因为它们只需要读取数据。
如果你有这个主意,请告诉我。如果需要,我可以分享更多代码。 :)