Expo应用中SQLite报错‘no such table: <tablename>’求助
Expo SQLite 报错:no such table: 解决指南
问题描述
在Expo应用中集成SQLite数据库时遇到以下错误:
→ Caused by: Error code: no such table:
]
ERROR [Error: Call to function 'NativeDatabase.prepareAsync' has been rejected.
最初通过自定义数据库拷贝逻辑解决了问题,但一段时间后错误复现。尝试使用SQLiteProvider的assetSource参数的官方写法也无法解决。仅更新了Android Studio模拟器和依赖包后,错误在模拟器和安卓真机上均出现。
项目依赖版本:
"dependencies": { "@expo-google-fonts/changa": "^0.2.3", "@expo-google-fonts/kanit": "^0.2.3", "@expo-google-fonts/maven-pro": "^0.2.3", "@expo/vector-icons": "^14.0.2", "expo": "~51.0.17", "expo-asset": "~10.0.10", "expo-constants": "~16.0.2", "expo-file-system": "~17.0.1", "expo-font": "~12.0.7", "expo-linking": "~6.3.1", "expo-router": "~3.5.17", "expo-sqlite": "~14.0.4", "expo-status-bar": "~1.12.1", "react": "18.2.0", "react-native": "0.74.2", "react-native-safe-area-context": "4.10.1", "react-native-screens": "3.31.1" },
之前使用的自定义拷贝代码:
import { Suspense, useEffect } from "react"; import { Text } from "react-native"; import { Stack } from "expo-router"; import { Asset } from "expo-asset"; import { SQLiteProvider } from "expo-sqlite"; import * as FileSystem from "expo-file-system"; export default function RootLayout() { const configDatabase = async () => { const dbName = "database.db"; const dbAsset = require("../assets/data/database.db"); const dbUri = Asset.fromModule(dbAsset).uri; const dbFilePath = `${FileSystem.documentDirectory}SQLite/${dbName}`; const fileInfo = await FileSystem.getInfoAsync(dbFilePath); if (!fileInfo.exists) { await FileSystem.makeDirectoryAsync( `${FileSystem.documentDirectory}SQLite/`, { intermediates: true } ); await FileSystem.downloadAsync(dbUri, dbFilePath); } }; useEffect(() => { const fetchDatabase = async () => { try { await configDatabase(); } catch (err) { console.error(err); } }; fetchDatabase(); }, []); return ( <Suspense fallback={<Text>Loading...</Text>}> <SQLiteProvider databaseName="database.db" useSuspense={true}> <Stack> <Stack.Screen name="index" options={{ headerShown: false, }} /> </Stack> </SQLiteProvider> </Suspense> ); }
尝试的官方简化代码:
export default function RootLayout() { return ( <Suspense fallback={<Text>Loading...</Text>}> <SQLiteProvider databaseName="database.db" useSuspense={true} assetSource={{ assetId: require("../assets/data/database.db") }} > <Stack> <Stack.Screen name="index" options={{ headerShown: false, }} /> </Stack> </SQLiteProvider> </Suspense> ); }
排查与解决步骤
1. 确认数据库文件完整性
- 用SQLite浏览器打开
../assets/data/database.db,验证目标表是否存在、结构是否正确。 - 在
app.json中添加配置,防止Expo压缩损坏SQLite文件:
或明确指定数据库文件的打包规则:"assetBundlePatterns": [ "**/*", "!**/*.db" ]"assetBundlePatterns": [ "assets/data/*.db" ]
2. 强制替换旧数据库文件
原逻辑仅在文件不存在时拷贝,若旧数据库已损坏或结构不一致,需强制替换。修改自定义拷贝逻辑:
const configDatabase = async () => { const dbName = "database.db"; const dbAsset = require("../assets/data/database.db"); const dbUri = Asset.fromModule(dbAsset).uri; const dbFilePath = `${FileSystem.documentDirectory}SQLite/${dbName}`; // 开发阶段强制删除旧文件,确保使用最新数据库 if (__DEV__) { await FileSystem.deleteAsync(dbFilePath, { idempotent: true }); } const fileInfo = await FileSystem.getInfoAsync(dbFilePath); if (!fileInfo.exists) { await FileSystem.makeDirectoryAsync(`${FileSystem.documentDirectory}SQLite/`, { intermediates: true }); await FileSystem.downloadAsync(dbUri, dbFilePath); } };
3. 确保数据库初始化完成后再连接
原useEffect的异步操作可能晚于SQLiteProvider初始化,导致连接空数据库。改用状态控制渲染时机:
import { Suspense, useEffect, useState } from "react"; import { Text, View } from "react-native"; import { Stack } from "expo-router"; import { Asset } from "expo-asset"; import { SQLiteProvider } from "expo-sqlite"; import * as FileSystem from "expo-file-system"; export default function RootLayout() { const [dbReady, setDbReady] = useState(false); const configDatabase = async () => { const dbName = "database.db"; const dbAsset = require("../assets/data/database.db"); const dbUri = Asset.fromModule(dbAsset).uri; const dbFilePath = `${FileSystem.documentDirectory}SQLite/${dbName}`; if (__DEV__) { await FileSystem.deleteAsync(dbFilePath, { idempotent: true }); } const fileInfo = await FileSystem.getInfoAsync(dbFilePath); if (!fileInfo.exists) { await FileSystem.makeDirectoryAsync(`${FileSystem.documentDirectory}SQLite/`, { intermediates: true }); await FileSystem.downloadAsync(dbUri, dbFilePath); } setDbReady(true); }; useEffect(() => { configDatabase().catch(console.error); }, []); if (!dbReady) { return <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}><Text>Loading database...</Text></View>; } return ( <Suspense fallback={<Text>Loading...</Text>}> <SQLiteProvider databaseName="database.db" useSuspense={true}> <Stack> <Stack.Screen name="index" options={{ headerShown: false }} /> </Stack> </SQLiteProvider> </Suspense> ); }
4. 检查Android文件系统权限
在app.json的android配置中添加必要权限:
"android": { "permissions": ["READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE"], "usesCleartextTraffic": true }
5. 清理缓存与重建项目
- 运行
npx expo start --clear清除Expo缓存 - 卸载模拟器/真机上的旧应用,重新安装
- 清除Android Studio模拟器缓存:模拟器菜单 → 更多 → 清除数据
内容的提问来源于stack exchange,提问作者ImEmo
相关产品推荐
相关产品推荐

