我知道这个问题已被问过很多次,但我看不出我做错了什么。我有一个包含两个表的数据库。
public class MoviesDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
private static final int DATABASE_VERSION = 14;
public static final String DATABASE_NAME = "popular_movies.db";
public MoviesDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
final String SQL_CREATE_MOVIE__SETTING_TABLE = "CREATE TABLE " + MovieContract.MovieSettings.TABLE_NAME + " (" +
MovieContract.MovieEntry._ID + " INTEGER PRIMARY KEY, " +
MovieContract.MovieSettings.COL_MOVIE_SETTING + " TEXT UNIQUE NOT NULL " +
" );";
final String SQL_CREATE_MOVIE_TABLE = "CREATE TABLE " + MovieContract.MovieEntry.TABLE_NAME + " (" +
MovieContract.MovieEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
MovieContract.MovieEntry.COL_MOVIE_KEY + " INTEGER NOT NULL, " +
MovieContract.MovieEntry.COL_MOVIE_ID + " INTEGER NOT NULL, " +
MovieContract.MovieEntry.COL_IS_ADULT + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COL_BACKDROP_PATH + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COL_ORIGINAL_LANGUAGE + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COL_ORIGINAL_TITLE + " TEXT NOT NULL," +
MovieContract.MovieEntry.COL_OVERVIEW + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COL_TITLE + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COL_IS_VIDEO + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COL_VOTE_AVERAGE + " REAL NOT NULL, " +
MovieContract.MovieEntry.COL_VOTE_COUNT + " INTEGER NOT NULL, " +
MovieContract.MovieEntry.COL_POSTER_PATH + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COL_DATE + " TEXT NOT NULL, " +
// Set up the location column as a foreign key to location table.
" FOREIGN KEY (" + MovieContract.MovieEntry.COL_MOVIE_KEY + ") REFERENCES " +
MovieContract.MovieSettings.TABLE_NAME + " (" + MovieContract.MovieSettings._ID + "), " +
// To assure the application have just one weather entry per day
// per location, it's created a UNIQUE constraint with REPLACE strategy
" UNIQUE (" + MovieContract.MovieEntry.COL_DATE + ", " +
MovieContract.MovieEntry.COL_MOVIE_KEY + ") ON CONFLICT REPLACE);";
sqLiteDatabase.execSQL(SQL_CREATE_MOVIE__SETTING_TABLE);
sqLiteDatabase.execSQL(SQL_CREATE_MOVIE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
// Note that this only fires if you change the version number for your database.
// It does NOT depend on the version number for your application.
// If you want to update the schema without wiping data, commenting out the next 2 lines
// should be your top priority before modifying this method.
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + MovieContract.MovieSettings.TABLE_NAME);
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + MovieContract.MovieEntry.TABLE_NAME);
onCreate(sqLiteDatabase);
}
}
所有空格似乎都是正确的 我通过内容提供商阅读存储的数据。
package tziomakas.theo.popularmoviespart2.data;
import android.annotation.TargetApi;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Created by theodosiostziomakas on 04/11/2017.
*/
public class MovieProvider extends ContentProvider {
private static final UriMatcher sUriMatcher = buildUriMatcher();
public static final int MOVIE = 100;
public static final int MOVIE_WITH_SETTING = 101;
public static final int MOVIE_WITH_SETTING_AND_DATE = 102;
public static final int SETTING = 200;
private static final SQLiteQueryBuilder sMovieBySettingQueryBuilder;
private MoviesDbHelper mOpenHelper;
static{
sMovieBySettingQueryBuilder = new SQLiteQueryBuilder();
//This is an inner join which looks like
//weather INNER JOIN location ON weather.location_id = location._id
sMovieBySettingQueryBuilder.setTables(
MovieContract.MovieEntry.TABLE_NAME + "INNER JOIN" +
MovieContract.MovieSettings.TABLE_NAME + "ON" +
MovieContract.MovieEntry.TABLE_NAME + "." + MovieContract.MovieEntry.COL_MOVIE_KEY + " = " +
MovieContract.MovieSettings.TABLE_NAME + MovieContract.MovieSettings._ID);
}
//movie_setting.settings = ?
private static final String sMovieSettingSelection =
MovieContract.MovieSettings.TABLE_NAME+
"." + MovieContract.MovieSettings.COL_MOVIE_SETTING + " = ? ";
//movie_setting.settings = ? AND date => ?
private static final String sMovieSettingWithStartDateSelection =
MovieContract.MovieSettings.TABLE_NAME+
"." + MovieContract.MovieSettings.COL_MOVIE_SETTING + " = ? AND " +
MovieContract.MovieEntry.COL_DATE + " >= ? ";
//movie_setting.settings = ? AND date = ?
private static final String sMovieSettingAndDateSelection =
MovieContract.MovieSettings.TABLE_NAME+
"." + MovieContract.MovieSettings.COL_MOVIE_SETTING + " = ? AND " +
MovieContract.MovieEntry.COL_DATE + " = ? ";
public Cursor getMovieBySetting(Uri uri, String[] projection, String sortOrder){
String movieSetting = MovieContract.MovieEntry.getMovieSettingFromUri(uri);
String [] selectionArgs;
String selection;
selection = sMovieSettingSelection;
selectionArgs = new String[]{movieSetting};
return sMovieBySettingQueryBuilder.query(mOpenHelper.getReadableDatabase(),
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
}
static UriMatcher buildUriMatcher() {
// I know what you're thinking. Why create a UriMatcher when you can use regular
// expressions instead? Because you're not crazy, that's why.
// All paths added to the UriMatcher have a corresponding code to return when a match is
// found. The code passed into the constructor represents the code to return for the root
// URI. It's common to use NO_MATCH as the code for this case.
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = MovieContract.CONTENT_AUTHORITY;
// For each type of URI you want to add, create a corresponding code.
matcher.addURI(authority, MovieContract.PATH_MOVIES, MOVIE);
matcher.addURI(authority, MovieContract.PATH_MOVIES + "/*", MOVIE_WITH_SETTING);
matcher.addURI(authority, MovieContract.PATH_MOVIE_SETTINGS, SETTING);
return matcher;
}
@Override
public boolean onCreate() {
mOpenHelper = new MoviesDbHelper(getContext());
return true;
}
@Override
public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// Here's the switch statement that, given a URI, will determine what kind of request it is,
// and query the database accordingly.
Cursor retCursor;
switch (sUriMatcher.match(uri)){
case MOVIE_WITH_SETTING:{
retCursor = getMovieBySetting(uri,projection,sortOrder);
break;
}
case MOVIE:{
retCursor = mOpenHelper.getReadableDatabase().query(
MovieContract.MovieEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
case SETTING:{
retCursor = mOpenHelper.getReadableDatabase().query(
MovieContract.MovieSettings.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
@Override
public String getType(Uri uri) {
// Use the Uri Matcher to determine what kind of URI this is.
final int match = sUriMatcher.match(uri);
switch (match) {
// Student: Uncomment and fill out these two cases
case MOVIE_WITH_SETTING_AND_DATE:
return MovieContract.MovieEntry.CONTENT_ITEM_TYPE;
case MOVIE_WITH_SETTING:
return MovieContract.MovieEntry.CONTENT_TYPE;
case MOVIE:
return MovieContract.MovieEntry.CONTENT_TYPE;
case SETTING:
return MovieContract.MovieSettings.CONTENT_TYPE;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
Uri returnUri;
switch (match) {
case MOVIE: {
normalizeDate(values);
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, values);
if ( _id > 0 )
returnUri = MovieContract.MovieEntry.buildMovieUri(_id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
}
case SETTING: {
long _id = db.insert(MovieContract.MovieSettings.TABLE_NAME, null, values);
if ( _id > 0 )
returnUri = MovieContract.MovieSettings.buildMovieSettingUri(_id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return returnUri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsDeleted;
// this makes delete all rows return the number of rows deleted
if ( null == selection ) selection = "1";
switch (match) {
case MOVIE:
rowsDeleted = db.delete(
MovieContract.MovieEntry.TABLE_NAME, selection, selectionArgs);
break;
case SETTING:
rowsDeleted = db.delete(
MovieContract.MovieSettings.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// Because a null deletes all rows
if (rowsDeleted != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsDeleted;
}
@Override
public int update(
Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsUpdated;
switch (match) {
case MOVIE:
normalizeDate(values);
rowsUpdated = db.update(MovieContract.MovieEntry.TABLE_NAME,values, selection, selectionArgs);
break;
case SETTING:
rowsUpdated = db.update(MovieContract.MovieSettings.TABLE_NAME, values, selection,
selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsUpdated;
}
private void normalizeDate(ContentValues values) {
// normalize the date value
if (values.containsKey(MovieContract.MovieEntry.COL_DATE)) {
long dateValue = values.getAsLong(MovieContract.MovieEntry.COL_DATE);
values.put(MovieContract.MovieEntry.COL_DATE, MovieContract.normalizeDate(dateValue));
}
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIE:
db.beginTransaction();
int returnCount = 0;
try {
for (ContentValues value : values) {
normalizeDate(value);
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, value);
if (_id != -1) {
returnCount++;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
getContext().getContentResolver().notifyChange(uri, null);
return returnCount;
default:
return super.bulkInsert(uri, values);
}
}
// You do not need to call this method. This is a method specifically to assist the testing
// framework in running smoothly. You can read more at:
// http://developer.android.com/reference/android/content/ContentProvider.html#shutdown()
@Override
@TargetApi(11)
public void shutdown() {
mOpenHelper.close();
super.shutdown();
}
}
这些代码行给出了以下错误。
//movie_setting.settings = ?
private static final String sMovieSettingSelection =
MovieContract.MovieSettings.TABLE_NAME+
"." + MovieContract.MovieSettings.COL_MOVIE_SETTING + " = ? ";
near ".": syntax error
您看到的问号是选择值 在我的例子中,它是一个名为popular的字符串。