You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:54:51