【问题标题】:Pre-packaged database has an invalid schema error预打包的数据库存在无效架构错误
【发布时间】:2019-07-15 07:59:01
【问题描述】:

我正在基于一个旧的 Android 项目构建一个 Android 应用程序。 在我的新应用程序中,我使用的是 Room。我必须使用第一个项目中使用的相同数据库。 此外,我使用 com.amitshekhar.android:debug-db 库从第一个项目中提取了数据库。 获得数据库文件后,我想用 Room 打开它。

我正在构建这样的数据库:

Room.databaseBuilder(
            androidContext(),
            Database::class.java, "database.db"
        ).createFromAsset("database.db")
            .build()

目前我正在使用这个 createFromAsset() 方法,虽然稍后我会使用 createFromFile() 方法,因为我的数据库应该从服务器下载。

但我得到 java.lang.IllegalStateException: Pre-packaged database has an invalid schema 发生这种情况是因为数据库中有几种数据类型在 Room 中不受支持,例如 NVARCHAR(200)、DATE 或 bit。

我知道 Room 只使用了五种 Sql 类型,但我不知道如何更改,以便 Room 可以使用上述方法打开这种数据库。

问题是如何将NVARCHAR(200)、DATE或bit转换成Room支持的数据类型?

【问题讨论】:

  • 谢谢,但我在上述问题的答案中看不到解决方案。显然,实现空迁移没有任何作用,因为方案不匹配。
  • 有时查看其他回复也很好,除了批准的答案。对于您的情况,this response 确实为解决方案提供了指导,因为迁移并不是那么简单
  • 您使用的是哪个版本的 Room?
  • @barq 2.2.0-alpha01

标签: android database sqlite android-room android-room-prepackageddatabase


【解决方案1】:

您必须将数据库转换为使用 Room 支持且与实体匹配的特定列类型关联。

对于 NVARCHAR(200),您需要将 TEXT 替换为将列定义为字符串的实体。

对于 DATE,如果您使用基于字符串的日期,则取决于实体定义,例如YYYY-MM-DD hh:mm:ss 那么实体应该是字符串和列关联TEXT。如果将日期存储为时间戳,则实体应该很长并且列关联 整数

答案在这里 Can't migrate a table to Room do to an error with the way booleans are saved in Sqlite 进行转换以将 BOOL 更改为 INTEGER。

您可以对此进行调整(尽管我对 DATE 持谨慎态度)以适应。

附加

您可能会发现以下内容很有用。您可以在您最喜欢的 SQLite 管理器工具中针对预先存在的数据库运行它。

WITH potentialRoomChanges AS (
    SELECT sm.name AS tablename, pti.name AS columnname, pti.type, dflt_value, pk,
        CASE 
            WHEN instr(upper(pti.type),'INT') THEN 'INTEGER'
            WHEN instr(upper(pti.type),'CHAR') OR instr(upper(pti.type),'CLOB') OR instr(upper(pti.type),'TEXT') THEN 'TEXT'
            WHEN instr(upper(pti.type),'BLOB') THEN 'BLOB'
            WHEN instr(upper(pti.type),'REAL') OR instr(upper(pti.type),'FLOA') OR instr(upper(pti.type),'DOUB') THEN 'REAL'
            ELSE 'NUMERIC'
        END AS roomtype ,
        CASE WHEN pti.[notnull] THEN 'Investigate NOT NULL USE' END AS nnindicator,
        sql
    FROM sqlite_master AS sm JOIN pragma_table_info(sm.name) AS pti
    WHERE 
        sm.type = 'table' 
        AND sm.name NOT LIKE 'sqlite_%' 
        AND sm.name <> 'android_metadata' 
        AND (
            upper(pti.type) <> roomtype 
            OR instr(roomtype,'NUMERIC') 
            OR nnindicator IS NOT NULL
            OR dflt_value IS NOT NULL
            OR pk > 0
        )
    ORDER BY sm.name,pti.cid
)
SELECT tablename, columnname, type, roomtype, 
CASE WHEN upper(type) <> upper(roomtype) THEN 'Investigate TYPE should be ' ||roomtype END AS typechange_notes,
CASE WHEN roomtype = 'NUMERIC' THEN 'Investigate NUMERIC' END AS numeric_notes, 
CASE WHEN dflt_value IS NOT NULL THEN 'Investigate DEFAULT VALUE of '||dflt_value END AS default_notes,
CASE WHEN pk > 0 THEN 'Investigate PRIMARY KEY inclusion' END AS primarykey_notes,
nnindicator AS notnull_notes 
FROM potentialRoomChanges
;

示例输出:-

希望列/文本是不言自明的。这基于定义的列类型(可能与使用的类型不同)。例如浮点数(显示第 5 行)您会认为是真实的。然而,根据派生类型关联,第一个规则(如果类型包括 INT,则为 INTEGER)已被应用。

按照Datatypes In SQLite Version 3 - 3.1. Determination Of Column Affinity的规则。

NUMERIC根据我对房间的有限经验,它不是它使用的类型,因此应始终将其更改为其他类型之一。

【讨论】:

  • 谢谢。这可能是唯一的方法。问题是我有 100 多个表和几种数据类型要更改。所以我不会走这条路。我会尽力在数据库创建期间进行这些更改(尽管我不会创建它)。
  • @bromden 也许另一种方法是允许空间创建数据库(空)关闭它,然后复制预先存在的。在房间外打开并使用INSERT INTO room_table SELECT * FROM preexisting_table 复制数据,其中表是从 sqlite_master 中提取的。完成后,可以删除预先存在的内容。
  • 我也会试试的。谢谢
  • @bromden 您可能会发现 this question and answer 有用。
  • 谢谢。我经历了它。我真的很想解决这些迁移问题,只需在生成的 sqlite 和我的 Room 中使用相同的、精确的方案即可。如果这不起作用,我将把这个 attach 方法作为 B 计划。
【解决方案2】:

在 Pojo(实体)类的每个字段之前使用 @NonNull
无需在主键字段中添加@NonNull
下面是一个例子

@Entity(tableName = "station")
public class Station {
    @PrimaryKey
    private int id;
    @NonNull
    private String name;
    @NonNull
    private int line;
    @NonNull
    private double lat;
    @NonNull
    private double lon;

    ... constructor, getters and setters
}

【讨论】:

  • 感谢您的回答,但恐怕我不确定这与包含带有 Room 的 NVARCHAR 的打开数据库有何关系。
【解决方案3】:

对我来说,问题是 Not-Null ,对于数据库中带有 NOT NULL 的每一列,它应该反映在带有 @NonNull 的模型中。

如果你LastName在数据库中是

"LastName"  TEXT NOT NULL,

在你的模型代码中应该是

@NonNull
private String LastName;

【讨论】:

    猜你喜欢
    • 2021-03-02
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 2022-01-10
    相关资源
    最近更新 更多