Android SQLite查询语句正确但Cursor为空问题求助
我正在开发一个帮朋友祖母管理糖尿病的App:用户输入血糖值(cukier),程序自动获取检测时间(godzina),返回对应的胰岛素类型(insulina)、剂量(dawka)及采血部位(cialo)等结果(wynik)。不过遇到了一个奇怪的问题,想请大家帮忙看看。
相关代码片段
(注:变量未遵循英文命名规范,因项目为快速开发)
public Wynik getData(int pomiar, int godzina) { Wynik wynik = null; String query = "SELECT * FROM " + "insulina" + " WHERE godzina = " + godzina + " AND cukier = " + pomiar; SQLiteDatabase db = this.getReadableDatabase(); try (Cursor cursor = db.rawQuery(query, null)) { Log.i("tag", "cursor length:" + cursor.getCount()); if (cursor.getCount() > 0) { String s = DatabaseUtils.dumpCursorToString(cursor); Log.d("s", s); cursor.moveToFirst(); wynik = new Wynik ( cursor.getInt(cursor.getColumnIndexOrThrow("godzina")), cursor.getString(cursor.getColumnIndexOrThrow("insulina")), cursor.getInt(cursor.getColumnIndexOrThrow("dawka")), cursor.getString(cursor.getColumnIndexOrThrow("cialo")) ); } cursor.close(); return wynik; } }
数据库结构说明
insulina表包含以下字段:
godzina:整数类型,存储检测时间(例:18)cukier:整数类型,存储血糖值(例:10)insulina:字符串类型,胰岛素类型dawka:整数类型,胰岛素剂量cialo:字符串类型,采血部位
问题详情
举个例子,当血糖值为10、检测时间取最近的18点时,生成的查询语句是:
SELECT * FROM insulina WHERE godzina = 18 AND cukier = 10
我在DB Browser for SQLite里执行这条语句能得到正确结果,但在App中执行时,Cursor.getCount()始终返回0,Android Studio已经提示数据库成功打开,我实在找不到问题所在,急需大家的帮助!
可能的排查方向和解决方案
嘿,我来帮你梳理下可能的原因和解决办法:
确认App使用的数据库和DB Browser打开的是同一个文件
Android的SQLite数据库默认存在/data/data/[你的应用包名]/databases/目录下,很多时候我们会把预填充的数据库文件复制到这个路径,但如果操作失误,App连接的可能是空的/未填充的数据库,而你在DB Browser里打开的是本地的原始文件。你可以通过Android Studio的Device File Explorer找到App的数据库文件,导出后用DB Browser打开,看看里面有没有数据。改用参数化查询避免拼接错误
直接拼接SQL语句很容易因为类型转换、空格或者隐形字符导致查询异常,而且还存在SQL注入风险。把你的查询改成参数化形式试试:
public Wynik getData(int pomiar, int godzina) { Wynik wynik = null; // 使用占位符?代替直接拼接变量 String query = "SELECT * FROM insulina WHERE godzina = ? AND cukier = ?"; SQLiteDatabase db = this.getReadableDatabase(); // 将变量作为字符串数组传入,rawQuery会自动处理类型转换 try (Cursor cursor = db.rawQuery(query, new String[]{String.valueOf(godzina), String.valueOf(pomiar)})) { Log.i("tag", "cursor length:" + cursor.getCount()); if (cursor.moveToFirst()) { // 建议用moveToFirst替代getCount判断,更高效 wynik = new Wynik ( cursor.getInt(cursor.getColumnIndexOrThrow("godzina")), cursor.getString(cursor.getColumnIndexOrThrow("insulina")), cursor.getInt(cursor.getColumnIndexOrThrow("dawka")), cursor.getString(cursor.getColumnIndexOrThrow("cialo")) ); } return wynik; } }
另外,try-with-resources语法会自动关闭Cursor,所以不需要手动调用cursor.close(),避免重复关闭导致异常。
- 打印完整的SQL语句对比
在执行查询前,把生成的SQL语句打印出来,和你在DB Browser里执行的完全对比:
Log.d("DebugSQL", "执行的SQL:" + query);
看看打印出来的语句是不是和你手动执行的一模一样,比如有没有可能pomiar或者godzina传入的数值和你预期的不一样?
- 确认数据库初始化/预填充是否成功
如果你的数据库是需要预填充数据的,检查初始化逻辑是否正确执行,比如在SQLiteOpenHelper的onCreate或onUpgrade里有没有正确导入数据,或者复制数据库文件的代码有没有报错。
内容的提问来源于stack exchange,提问作者Damian Ziółkowski

