expo sqlite 使用现有数据库

Posted

技术标签:

【中文标题】expo sqlite 使用现有数据库【英文标题】:expo sqlite use existing database 【发布时间】:2019-09-01 11:48:25 【问题描述】:

根据 react-native 的 expo sqlite 文档,我可以像这样初始化一个数据库:

const db = SQLite.openDatabase('db.db');

这行得通,我可以像这样更新数据库:

  update() 
    db.transaction(tx => 
      tx.executeSql(
        `select * from items where done = ?;`,
        [this.props.done ? 1 : 0],
        (_,  rows:  _array  ) => this.setState( items: _array )
      );
    );
  

根据我有限的理解,这会在设备中创建一个数据库。然后它被操纵保持所有数据库本地。

我有一个数据库,其中已经设置了所有必要的表。如何让它使用我已经设置的当前数据库?

例如:(语法不正确)

const db = SQLite.openDatabaseIShipWithApp('mypath/mydb.db');

我找不到任何文档来帮助我解决这个问题。 我提到上面的唯一原因是因为我已经有了带有表和数据的数据库。

任何帮助将不胜感激!

【问题讨论】:

【参考方案1】:

我能够通过使用 expo 的 FileSystem.downloadAsync 来实现这一点:

首先我导入它,因为我使用的是 expo 托管应用程序:

import  FileSystem  from 'expo';

然后我像这样从服务器下载它:

// load DB for expo
FileSystem.downloadAsync(
  'http://example.com/downloads/data.sqlite',
  FileSystem.documentDirectory + 'data.sqlite'
)
.then(( uri ) => 
  console.log('Finished downloading to ', uri)
)
.catch(error => 
  console.error(error);
)

第一个参数是位置的 uri,第二个参数是我想要放置的位置。这里我使用的是 documentDirectory。

【讨论】:

你什么时候在你的应用程序中调用它?在 App.js 中?我只有在 iPad 上遇到这个问题,在第一次安装和执行时,执行对数据库的调用不起作用,并且数据库中没有表。重新加载时,一切正常 我在app.js之后的一个组件中调用它 我发现这在最新版本的世博会中也不起作用,文件系统现在完全属于expo-file-system【参考方案2】:

如果在资产中使用本地预填充数据库:

import * as FileSystem from "expo-file-system";
import Asset from "expo-asset";

async function openDatabaseIShipWithApp() 
    const internalDbName = "dbInStorage.sqlite"; // Call whatever you want
    const sqlDir = FileSystem.documentDirectory + "SQLite/";
    if (!(await FileSystem.getInfoAsync(sqlDir + internalDbName)).exists) 
        await FileSystem.makeDirectoryAsync(sqlDir, intermediates: true);
        const asset = Asset.fromModule(require("../assets/database/mydb.sqlite"));
        await FileSystem.downloadAsync(asset.uri, sqlDir + internalDbName);
    
    this.database = SQLite.openDatabase(internalDbName);

这将创建SQLite 目录和数据库(如果不存在)。否则FileSystem.downloadAsync() 将在新安装的应用程序上抛出错误。

一些备注:

您不能在require() 中使用变量(仅限字符串)。参见例如this。

您必须明确允许在 Expo 中加载文件扩展名 .db.sqlite,请参阅 this。你必须在根目录下创建一个文件metro.config.js

const defaultAssetExts = require("metro-config/src/defaults/defaults").assetExts;

module.exports = 
    resolver: 
        assetExts: [
            ...defaultAssetExts,
            "db", "sqlite"
        ]
    
;
并且可以在app.json 中添加以下内容
"expo": 
  "assetBundlePatterns": [
    "**/*"
  ]

如果要删除加载的数据库(例如用于测试),您必须在手机设置中清除整个 Expo App 数据(删除缓存不够)。或者写一个这样的方法:
async function removeDatabase() 
    const sqlDir = FileSystem.documentDirectory + "SQLite/";
    await FileSystem.deleteAsync(sqlDir + "dbInStorage.sqlite", idempotent: true);

【讨论】:

虽然这个答案让我最接近成功构建应用程序,但我在最新的世博会版本中遇到的错误是我的发货数据库无法找到,因为没有匹配的扩展名,即使使用更新的 Metro。 config.js 对不起,可以依赖很多东西。可以再次查看this issue。如果您找到解决方案,您可能想评论或编辑我的答案。谢谢 :) 谢谢,但这对我来说也不是解决方案。【参考方案3】:

这很简单 如果您捆绑您的应用程序,您必须首先将数据库从asset 文件夹移动到document directory。为此,请检查是否存在名为 SQLite 的文件夹。如果没有,请创建它。为什么需要一个名为SQLite 的文件夹?这是因为SQLite.openDatabase(databaseName) 默认在FileSystem.documentDirectory + 'SQLite' 中显示。然后,创建文件夹后,您可以从asset 文件夹下载database。确保您的 database 位于名为 asset 的文件夹中。在您的应用程序文档树的src/asset 下找到文件夹asset。另外,请确保配置您的app.jsonmetro.config.js

import * as SQLite from 'expo-sqlite';
import * as FileSystem from 'expo-file-system';
import  Asset  from 'expo-asset';

const FOO = 'foo.db'

if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + 'SQLite')).exists) 
  await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + 'SQLite');
;

await FileSystem.downloadAsync(
    // the name 'foo.db' is hardcoded because it is used with require()
    Asset.fromModule(require('../../asset/foo.db')).uri,
    // use constant FOO constant to access 'foo.db' whereever possible
    FileSystem.documentDirectory + `SQLite/$FOO`
);

// Then you can use the database like this
SQLite.openDatabase(FOO).transaction(...);


// app.json

  "name": "Your App name",
  "displayName": "Your App name",
  "assetBundlePatterns": [
    "assets/**"
  ],
  "packagerOpts": 
    "assetExts": ["db"]
  


// metro config
const  getDefaultConfig  = require('@expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);

module.exports = 
  resolver: 
    assetExts: [...defaultConfig.resolver.assetExts, 'db', 'json'],
  ,
  transformer: 
    getTransformOptions: async () => (
      transform: 
        experimentalImportSupport: false,
        inlineRequires: false,
      ,
    ),
  ,
;

这都是从expo的文档中提取的。

【讨论】:

【参考方案4】:

我不相信这在世博会上是可能的。如果您使用的是裸 android 项目,则有一种使用现有数据库的方法,其中涉及编写一些本机代码以将数据库从项目资产复制到手机上的标准位置(/data/ 等)以供您的应用程序使用。

https://medium.com/@johann.pardanaud/ship-an-android-app-with-a-pre-populated-database-cd2b3aa3311f

我一直亲自使用 CREATE TABLE IF NOT EXISTS 创建数据库,因为 sqlite 要求您在查询之前定义模式。如果您需要为数据库播种,则在此步骤之后将执行其他步骤以插入所需的数据。

在大多数情况下,您还需要检查您的参考数据并定期从服务器更新它(它甚至可能从发布您的 apk 更改为下载应用程序的人),并且此代码在没有参考数据库中的数据。

有一些服务试图让你摆脱他的麻烦(例如解析),但你需要决定是否对它们托管你的数据感到满意。)我没有使用它们,所以不确定这是怎么回事工作正常,但我被告知它试图解决离线第一类问题。

请记住,在未来的迭代中,您可能需要修改未来版本的结构(添加字段等),因此您可能需要定义一些在应用程序首次启动时加载的代码,检查数据库版本并应用任何更改将数据库提升到适当级别所必需的。

【讨论】:

实际上可以使用:FileSystem.downloadAsync(),您可以下载一个数据库并将其设置在 documentDirectory 中,然后在那里使用它。 @Chanoch

以上是关于expo sqlite 使用现有数据库的主要内容,如果未能解决你的问题,请参考以下文章

如何在 expo react native 中使用 sqlite 文件

Expo SQLite 渲染无限数量的组件

AsyncStorage 与 Expo.SQLite

使用 react native 和 expo SQLite 在移动设备、iOS 和 Android 上存储大数据

如何使用 expo-sqlite 执行带有 typescript 的 sql?

如何获取 expo-sqlite 的类型?