嘿,伙计们我已经在这个问题上坚持了很长一段时间,但我似乎无法解决这个问题。
我尝试在我的Android应用程序中查询我的SQLite数据库中的一行,我有一些奇怪的行为。
我有以下单元测试:
public class DatabaseTest extends AndroidTestCase {
...
public void testSaveMeeting() {
Meeting meeting = new Meeting(1, MeetingStatus.ONGOING);
MeetingDataSource mds = new MeetingDataSource(getContext());
long meetingId = mds.save(meeting);
assertEquals(meeting, mds.getById(meetingId));
}
}
在这里,我尝试使用我的数据库映射器将模型对象meeting
保存到数据库中。以下代码段是我的模型Meeting
,它存储应保存到数据库的数据:
public class Meeting {
private long id;
private MeetingStatus meetingStatus;
public Meeting() {}
public Meeting(long meetingId, MeetingStatus meetingStatus) {
this.id = meetingId;
this.meetingStatus = meetingStatus;
}
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public void setMeetingStatus(MeetingStatus meetingStatus) { this.meetingStatus = meetingStatus; }
public MeetingStatus getMeetingStatus() { return meetingStatus; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Meeting meeting = (Meeting) o;
return id == meeting.id && meetingStatus == meeting.meetingStatus;
}
@Override
public int hashCode() {...}
}
我的MeetingDataSource是一个mapper类,我尝试将模型对象映射到数据库,反之亦然。该类如下所示:
public class MeetingDataSource extends DataSource<Meeting> {
private static String REQUEST_TABLE_NAME = DatabaseContract.MeetingTable.TABLE_NAME;
private static String[] ALL_COLUMNS = {
DatabaseContract.MeetingTable._ID,
DatabaseContract.MeetingTable.COLUMN_NAME_STATUS
};
public MeetingDataSource(Context context) { super(context); }
@Override
public Meeting cursorToObject(Cursor cursor) {
if(cursor != null && cursor.moveToFirst()) {
Meeting meeting = new Meeting();
meeting.setId(cursor.getLong(0));
meeting.setMeetingStatus(MeetingStatus.valueOfLabel(cursor.getString(1)));
return meeting;
}
return null;
}
@Override
protected ContentValues objectToContentValues(Meeting meeting) {
ContentValues values = new ContentValues();
values.put(DatabaseContract.MeetingTable._ID, meeting.getId());
values.put(DatabaseContract.MeetingTable.COLUMN_NAME_STATUS, meeting.getMeetingStatus().getLabel());
return values;
}
@Override
public Meeting getById(long id) {
try {
openReadConnection();
} catch (SQLException e) {
e.printStackTrace();
}
String where = DatabaseContract.MeetingTable._ID + " = ?";
String[] whereValues = { String.valueOf(id) };
// I think this is the part where my Problem is.
Cursor cursor = database.query(
REQUEST_TABLE_NAME,
ALL_COLUMNS,
where,
whereValues,
null,
null,
null);
Meeting meeting = cursorToObject(cursor);
closeConnection();
return meeting;
}
@Override
public long save(Meeting meeting) {
try {
openWriteConnection();
} catch (SQLException e) {
e.printStackTrace();
}
long rowId = database.insert(REQUEST_TABLE_NAME, null, objectToContentValues(meeting));
closeConnection();
return rowId;
}
@Override
public void update(Meeting meeting) {...}
@Override
public List<Meeting> getAll() {...}
}
此类扩展了以下适配器DataSource
。这是SQLiteOpenHelper
的包装器,用于创建数据库本身:
public abstract class DataSource<DatabaseTableType> {
protected Context context;
protected SQLiteDatabase database;
private DatabaseHelper dbHelper;
public DataSource(Context context) {
this.context = context;
dbHelper = new DatabaseHelper(context);
}
protected void openReadConnection() throws SQLException {
database = dbHelper.getReadableDatabase();
}
protected void openWriteConnection() throws SQLException { database = dbHelper.getWritableDatabase(); }
protected void closeConnection() {
dbHelper.close();
}
protected abstract DatabaseTableType cursorToObject(Cursor cursor);
protected abstract ContentValues objectToContentValues(DatabaseTableType object);
public abstract DatabaseTableType getById(long id);
public abstract List<DatabaseTableType> getAll();
public abstract long save(DatabaseTableType object);
public abstract void update(DatabaseTableType object);
}
现在这是奇怪的部分:
当我在方法MeetingDataSource
中的getById
中添加where语句和参数时,我在测试中的assertEquals失败了,因为我的游标是空的,因此cursorToObject
的返回值
但如果我把它留下这样:
Cursor cursor = database.query(
REQUEST_TABLE_NAME,
ALL_COLUMNS,
null,
null,
null,
null,
null);
我的测试成功,这让我相信cursorToObject
生成了正确的对象。
我也试过了rawQuery
:
Cursor cursor = database.rawQuery("SELECT * FROM " + REQUEST_TABLE_NAME + " WHERE _id = ?", whereValues);
也返回null
。而
Cursor cursor = database.rawQuery("SELECT * FROM " + REQUEST_TABLE_NAME, null);
返回右Meeting
并让测试通过。
我非常感谢任何答案:)
编辑1
迁移有助于了解我如何定义我的表以及SQLiteOpenHelper
的作用。
这是我的DatabaseHelper,它扩展SQLiteOpenHelper
以便在第一次访问时创建和拆除我的数据库:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "DatabaseHelper";
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "lma.db";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DatabaseContract.AttendanceStatusTable.SETUP_TABLE);
db.execSQL(DatabaseContract.MeetingStatusTable.SETUP_TABLE);
db.execSQL(DatabaseContract.MeetingTable.CREATE_TABLE);
db.execSQL(DatabaseContract.AttendanceTable.CREATE_TABLE);
db.execSQL(DatabaseContract.LocationTable.CREATE_TABLE);
db.execSQL(DatabaseContract.ContactPositionTable.CREATE_TABLE);
db.execSQL(DatabaseContract.RouteTable.CREATE_TABLE);
db.execSQL(DatabaseContract.StepTable.CREATE_TABLE);
db.execSQL(DatabaseContract.WarningTable.CREATE_TABLE);
db.execSQL(DatabaseContract.RoutesToWarningsTable.CREATE_TABLE);
db.execSQL(DatabaseContract.RoutesToStepsTable.CREATE_TABLE);
Log.i(TAG, "Database created");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DatabaseContract.RoutesToStepsTable.DROP_TABLE);
db.execSQL(DatabaseContract.RoutesToWarningsTable.DROP_TABLE);
db.execSQL(DatabaseContract.WarningTable.DROP_TABLE);
db.execSQL(DatabaseContract.StepTable.DROP_TABLE);
db.execSQL(DatabaseContract.RouteTable.DROP_TABLE);
db.execSQL(DatabaseContract.ContactPositionTable.DROP_TABLE);
db.execSQL(DatabaseContract.LocationTable.DROP_TABLE);
db.execSQL(DatabaseContract.AttendanceTable.DROP_TABLE);
db.execSQL(DatabaseContract.MeetingTable.DROP_TABLE);
db.execSQL(DatabaseContract.MeetingStatusTable.DROP_TABLE);
db.execSQL(DatabaseContract.AttendanceStatusTable.DROP_TABLE);
Log.i(TAG, "Database dropped");
onCreate(db);
}
}
在我的DatabaseContract中,我定义了所有SQL语句来创建我的表。它还定义了我的列名和表名:
public final class DatabaseContract {
private static final String INT_TYPE = " INTEGER";
private static final String VARCHAR_TYPE = " VARCHAR(25)";
private static final String COMMA = ", ";
private static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS ";
private static final String PRIMARY_KEY = " PRIMARY KEY";
private static final String NOT_NULL = " NOT NULL";
// To prevent someone from accidentally instantiating the contract class
public DatabaseContract() {}
// Enum Types
...
public static abstract class MeetingStatusTable implements BaseColumns {
public static final String TABLE_NAME = "meeting_statuses";
public static final String COLUMN_NAME_TYPE = "type";
public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + COLUMN_NAME_TYPE + VARCHAR_TYPE + PRIMARY_KEY + ");";
public static final String INSERT_DATA = "INSERT INTO " + TABLE_NAME + "(" + COLUMN_NAME_TYPE + ") " + "VALUES (\"NEW\"), (\"ONGOING\"), (\"TERMINATED\"), (\"COMPLETED\");";
public static final String SETUP_TABLE = CREATE_TABLE + INSERT_DATA;
public static final String DROP_TABLE = DROP_TABLE_STATEMENT + TABLE_NAME + ";";
}
// Data Tables
public static abstract class MeetingTable implements BaseColumns {
public static final String TABLE_NAME = "meetings";
public static final String COLUMN_NAME_STATUS = "status";
public static final String CREATE_TABLE =
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
_ID + INT_TYPE + PRIMARY_KEY + NOT_NULL + COMMA +
COLUMN_NAME_STATUS + VARCHAR_TYPE + NOT_NULL + COMMA +
" FOREIGN KEY (" + COLUMN_NAME_STATUS + ") REFERENCES " + MeetingStatusTable.TABLE_NAME + " (" + MeetingStatusTable.COLUMN_NAME_TYPE + ")" +
" );";
public static final String DROP_TABLE = DROP_TABLE_STATEMENT + TABLE_NAME + ";";
}
...
}
编辑2
在评论中,我被问到我的save
中的MeetingDataSource
方法是否实际返回了正确的值,但事实并非如此。通过此编辑,我在我的MeetingDataSource
中添加了我的保存方法代码,这似乎是真正的麻烦制造者。
答案 0 :(得分:0)
感谢下面的评论我的问题,我能够弄清楚出了什么问题。因为我正在测试我的数据库映射器,所以我接到了电话
mockContext.deleteDatabase(DatabaseHelper.DATABASE_NAME);
在我的setUp()方法中进行测试,以确保我的数据库在测试之前是空的。但是当我意识到在每次测试之前执行setUp()时我删除了这一行。由于我没有在每次测试运行后重新安装我的应用程序,因此数据库未被清理,因此插入尝试插入已存在于数据库中的数据,因此返回-1作为索引,因为我的表中的主键必须是唯一的。