Android Studio查询SQLite报no such table: Student错误排查
问题根因
你代码里有3个核心bug,直接导致预置库永远加载失败:
- 数据库拷贝路径完全错误:
copyDatabase()方法里输出路径只写了DB_NAME,没有拼接数据库目录路径,相当于把assets里的预置库写到了app私有根目录,SQLiteOpenHelper根本读不到这个文件。每次启动时SQLiteOpenHelper会自动在正确的databases目录下创建一个空的student.db,你onCreate()实现是空的,这个空库里没有任何表,查询自然报no such table: Student。 - 异常捕获静默吞错:
copyDatabase()里把所有异常都catch住只打日志,哪怕assets里找不到文件、输出路径无权限、拷贝中途失败,程序都不会中断,会直接继续用那个自动生成的空库跑逻辑。这就是为什么你删了assets下的预置库,还是报一模一样的错——空库早在第一次启动时就生成了,后续根本不会走到拷贝逻辑。 - 数据库存在校验逻辑不可靠:硬编码的
/data/user/0/...路径在多用户系统、低版本安卓、应用分身场景下不通用,且仅判断文件存在不校验文件有效性,哪怕是0字节的损坏空文件,都会被判定为「数据库已存在」,跳过拷贝流程。 - 额外说明:你写的
Student类是表对应的实体类,作用是把查询到的每行记录封装成Java对象,你现在直接拼接字符串返回查询结果,暂时用不到这个类,不影响运行。
修复步骤
- 先清除测试app的旧数据:去系统设置-应用列表找到这个测试app,选择「清除存储/清除数据」,或者直接卸载重装,把之前生成的错误空库删掉,避免干扰后续测试。
- 修正数据库路径获取逻辑,不要硬编码路径:
把MyDBHandler里硬编码DB_PATH的行删掉,改成动态初始化:
// 删除原硬编码行:private static String DB_PATH = "/data/user/0/com.example.testdatabase/databases/"; private static String DB_PATH;
在构造方法里动态获取系统实际的数据库存储路径,兼容所有安卓版本:
public MyDBHandler(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, DB_NAME, factory, DB_VERSION); this.mContext = context; // 动态获取数据库目录,自动适配不同系统版本、多用户场景 DB_PATH = mContext.getDatabasePath(DB_NAME).getParent() + File.separator; }
- 修正
copyDatabase()方法:补全输出路径,去掉吞错的全局异常捕获,记得关闭输入流:
private void copyDatabase() throws IOException{ InputStream mInputStream = mContext.getAssets().open(DB_NAME); // 补全输出路径,把预置库写到系统默认读取数据库的位置 String outFileName = DB_PATH + DB_NAME; OutputStream mOutputStream = new FileOutputStream(outFileName); byte[] buffer = new byte[1024]; int length; while((length = mInputStream.read(buffer)) > 0){ mOutputStream.write(buffer,0,length); } mOutputStream.flush(); mOutputStream.close(); mInputStream.close(); }
- 优化
checkDatabase()逻辑,不要只判断文件存在,同时校验数据库可正常打开:
private boolean checkDatabase(){ SQLiteDatabase checkDB = null; try { final String mPath = DB_PATH + DB_NAME; checkDB = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.OPEN_READONLY); } catch (SQLiteException e){ e.printStackTrace(); } if (checkDB != null){ checkDB.close(); return true; } return false; }
- 修正
createDatabase()逻辑,删掉自动生成的空库再执行拷贝:
你之前调用this.getReadableDatabase()会先生成一个空库占住位置,不删掉的话拷贝的预置库会写入失败:
public void createDatabase() throws IOException{ boolean mDatabaseExist = checkDatabase(); if (!mDatabaseExist){ this.getReadableDatabase(); this.close(); try{ // 删掉刚生成的空库,避免阻塞预置库写入 File emptyDbFile = new File(DB_PATH + DB_NAME); if (emptyDbFile.exists()) emptyDbFile.delete(); copyDatabase(); } catch(IOException mIOException){ mIOException.printStackTrace(); throw new Error("Error copying Database"); } finally { this.close(); } } }
后续注意事项
- 确认assets目录下的
student.db文件内确实存在名为Student的表,SQLite对表名大小写敏感,不要写错成student、STUDENT。 - 如果后续修改了assets内的预置数据库内容,要么手动清除app数据,要么升级
DB_VERSION版本号,否则已存在的有效数据库不会触发重新拷贝逻辑。 - 后续可以把查询到的Cursor结果封装成
Student对象列表,比手动拼接字符串更易维护,当前测试阶段不实现也不影响功能运行。
内容的提问来源于stack exchange,提问作者Stephen
相关产品推荐
相关产品推荐

