Android SQLite查询,WHERE无法正常工作

时间:2014-05-18 17:58:50

标签: android android-sqlite where-clause sqliteopenhelper

嘿,伙计们我已经在这个问题上坚持了很长一段时间,但我似乎无法解决这个问题。

我尝试在我的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中添加了我的保存方法代码,这似乎是真正的麻烦制造者。

1 个答案:

答案 0 :(得分:0)

感谢下面的评论我的问题,我能够弄清楚出了什么问题。因为我正在测试我的数据库映射器,所以我接到了电话

mockContext.deleteDatabase(DatabaseHelper.DATABASE_NAME);

在我的setUp()方法中进行测试,以确保我的数据库在测试之前是空的。但是当我意识到在每次测试之前执行setUp()时我删除了这一行。由于我没有在每次测试运行后重新安装我的应用程序,因此数据库未被清理,因此插入尝试插入已存在于数据库中的数据,因此返回-1作为索引,因为我的表中的主键必须是唯一的。