为啥 Room 不从资产中填充数据库?

Posted

技术标签:

【中文标题】为啥 Room 不从资产中填充数据库?【英文标题】:Why is Room not populating database from assets?为什么 Room 不从资产中填充数据库? 【发布时间】:2021-10-01 09:32:39 【问题描述】:

我正在尝试使用 Room 的 createFromAssets 方法从 assets 文件夹中复制预填充的 SQLite 数据库。该数据库相当大(一个表有大约 370k 条目;总大小约为 18mb)。数据库在databases 文件夹中创建,它包含适用的表,但没有数据传输到表中。

我查看了以下 SO 问题,但没有找到解决方案:

    How to pre-populate Room Database from asset? Room: Database not created How To recreate Room Database From Assets? android Room Library fails to copy database from Asset Room: Database not created

我还查看了以下信息页面:

    Prepopulate your Room database RoomDatabase.Builder Packing the Room: pre-populate your database with this one method Android Room with a View - Java - Codelab

我尝试等待几分钟,看看是否需要时间来填充数据库,但从未出现任何数据。我一直在使用 AS 的设备文件资源管理器从虚拟设备下载数据库并在 DB Browser for SQLite 中打开它,以及使用 AS 的 Database Inspector。

此外,LogCat 始终显示以下内容(每次登录 MainActivity):

D/MainActivity: onCreate 1st 10 good words NOT FOUND

GoodWordsAndFrequency.db 结构: 表 1 - “bad_words” 第 1 列 - “单词”,文本 表 2 - “english_words” 第 1 列 - “id”,整数 第 2 列 - “单词”,文本 第 3 列 - “频率”,整数

app build.gradle 依赖:

dependencies 

    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'junit:junit:4.13.1'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    // For Room database
    def room_version = "2.3.0"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    testImplementation "androidx.room:room-testing:$room_version"


ValidWords 实体:

import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "english_words")
public class ValidWords 
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    public int mID;

    @ColumnInfo(name = "word")
    @NonNull
    public String mWord;

    @ColumnInfo(name = "frequency")
    public int mFrequency;

    public ValidWords(@NonNull String word) this.mWord = word;

    public String getWord() return this.mWord;

    public void setWord(@NonNull String word) this.mWord = word;

    public int getFrequency() return mFrequency;

    public void setFrequency(int frequency) this.mFrequency = frequency;

BadWords 实体:

import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity (tableName = "bad_words")
public class BadWords 
    @PrimaryKey
    @NonNull
    @ColumnInfo(name = "word")
    public String mWord;

    public BadWords(@NonNull String word) this.mWord = word;

    @NonNull
    public String getWord() return mWord;

    public void setWord(@NonNull String mWord) this.mWord = mWord;

wordDAO 接口:

import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;

import java.util.List;

@Dao
public interface wordsDAO 
    @Query("SELECT * FROM english_words LIMIT 10")
    List<ValidWords> getFirstTenGoodWords();

    @Query("SELECT * FROM bad_words")
    List<BadWords> getBadWords();

    @Query("SELECT * FROM english_words WHERE word LIKE :wordToCheck LIMIT 1")
    ValidWords getMatch(String wordToCheck);

    @Insert(entity = ValidWords.class)
    long insertGoodWordInfo(ValidWords validWord);

ReferenceWordsDB 数据库:

import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;

@Database(entities = BadWords.class, ValidWords.class, version = 2)
public abstract class ReferenceWordsDB extends RoomDatabase 

    public abstract wordsDAO wordsDAO();

    private static volatile ReferenceWordsDB INSTANCE;

    static ReferenceWordsDB getDatabase(final Context context)
        if (INSTANCE == null)
            synchronized (ReferenceWordsDB.class)
                if (INSTANCE == null)
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            ReferenceWordsDB.class, "GoodWordsAndFrequency.db")
                            .createFromAsset("GoodWordsAndFrequency.db")
                            .fallbackToDestructiveMigration()
                            .allowMainThreadQueries()
                            .build();
                
            
        
        return INSTANCE;
    

主活动:

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.res.ResourcesCompat;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class MainActivity extends AppCompatActivity 

    private static ArrayList<ArrayList<ValidWords>> infoFromFindActualWordRunnableClass;
    private final String LOG_CLASS = "MainActivity";
    private ReferenceWordsDB wordsDB;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        final String METHOD = "onCreate";
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getViews();

        wordsDB = ReferenceWordsDB.getDatabase(this);
        List<ValidWords> first10GoodWords = new ArrayList<>(wordsDB.wordsDAO().getFirstTenGoodWords());

        if (first10GoodWords.size() == 0)
            Log.d(LOG_CLASS, String.format(Locale.US, "%s 1st 10 good words NOT FOUND", METHOD));
        
        for (ValidWords currentWord : first10GoodWords)
            Log.d(LOG_CLASS, String.format(Locale.US, "%s currentWord word = %s, frequency = %d", METHOD, currentWord.getWord(), currentWord.getFrequency()));
        

...
    

我正在使用安装了所有已知更新的 Android Studio 4.2.2。

【问题讨论】:

1) 删除.fallbackToDestructiveMigration() 行时会发生什么? 2) 在 Db Browser 中,当您选择 Edit Pragmas 时会显示什么 User Version。 3)源是否也有不为空(大于0字节)的GoodWordsAndFrequency.db-wal @MikeT 1) 如果我只是删除该行并运行应用程序,我会得到相同的结果。如果我还删除了“数据库”文件夹中的 3 个文件并重新运行它,我会得到“java.lang.IllegalStateException:需要从 1 迁移到 2,但未找到”。 2) 用户版本指示 2 表示我要从设备上取下的版本。让我检查一下我放入资产的源数据库... 3)我在“资产”文件夹中没有文件的 -wai 版本。设备上的“数据库”文件夹中有一个,它是 0db。仅供参考,设备上“数据库”文件夹中的 .db 文件为 18.4 Mb,但表为空?? 听起来像是资产文件夹中的版本 1 或 0。如果是这样,请使用 DB Brwoser 将版本更改为 2 并重试。 fallBackToDestructive 表示如果没有迁移则从新创建。 @MikeT 好吧,我觉得自己像个傻瓜。在源数据库中,我已将用户版本更改为 2,但忘记单击数据库浏览器底部的保存。它没有提示我编写更改,所以我认为它立即保存了 Pragmas 更改。在浪费一天时间研究问题并写下我的问题之前,应该已经直观地验证了源版本。谢谢你抓住它!想要提交答案,我会接受吗? 【参考方案1】:

据推测,可能的问题是数据库正在被破坏,因为迁移不存在与.fallbackToDestructiveMigration() 的使用一起使用,因此是空的但其他方面有效的数据库。

因此,您应该检查源数据库的版本(Edit Pragmas for Db Browesr 中的 user_version)。它必须与@Database 中代码中的版本相匹配。在您的情况下,您有 @Database(entities = BadWords.class, ValidWords.class, version = 2) 所以 user_version id DB Browser 应该是 2。

【讨论】:

以上是关于为啥 Room 不从资产中填充数据库?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Room Persistence Library 与预填充的数据库一起使用?

带有更新的预填充数据库的 Android Room 迁移

这段代码有啥问题?不从核心数据填充表! iPad

如何在首次运行时填充 Android Room 数据库表?

为啥数据不从视图传递到控制器

为啥 Room 实体不适用于 Android 中的不可变属性