[英]Issue Prepopulate Room Database - Database Inspector empty
I am new to Android development and I am trying to develop a very simple app.我是 Android 开发的新手,我正在尝试开发一个非常简单的应用程序。 I want to create a prepopulated database which has one table.我想创建一个有一个表的预填充数据库。 The table is about users and it has only three columns id, name, and profession.该表是关于用户的,它只有三列 id、name 和 profession。 The only usage of the database will be to search via the name of every user and find their profession.该数据库的唯一用途是通过每个用户的姓名进行搜索并找到他们的职业。 So I only need to prepopulate it with some data.所以我只需要用一些数据预先填充它。
My issue is that nothing is happening when i run the databse nothing is happening the database is not even been created.我的问题是,当我运行数据库时什么也没有发生,甚至没有创建数据库。 cant see anything in the dtabase inspector在数据库检查器中看不到任何东西
As I have read from the documentation of Room database https://developer.android.com/training/data-storage/room/prepopulate I just need to add the following code正如我从 Room 数据库https://developer.android.com/training/data-storage/room/prepopulate的文档中读到的,我只需要添加以下代码
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build();
So I created my small database in SqliteStudio and now I am trying to copy it with the Room database in Android. Bellow you can see a screenshot of my table users_table from sqliteStudio所以我在 SqliteStudio 中创建了我的小型数据库,现在我正在尝试将它复制到 Android 中的 Room 数据库。下面你可以看到我的表users_table 来自 sqliteStudio的屏幕截图
Dependencies依赖关系
// Room components
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
implementation 'androidx.wear:wear:1.1.0'
annotationProcessor "androidx.room:room-compiler:$rootProject.roomVersion"
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-viewmodel:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-common-java8:$rootProject.lifecycleVersion"
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
compileOnly 'com.google.android.wearable:wearable:2.8.1'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
gradle gradle
ext {
appCompatVersion = '1.3.0'
constraintLayoutVersion = '2.0.4'
coreTestingVersion = '2.1.0'
lifecycleVersion = '2.3.1'
materialVersion = '1.3.0'
roomVersion = '2.3.0'
// testing
junitVersion = '4.13.2'
espressoVersion = '3.1.0'
androidxJunitVersion = '1.1.2'
}
Bellow you can see also the code from my Dao, DatabaseClass, Entity, Repository, ViewModel在下面您还可以看到来自我的 Dao、DatabaseClass、Entity、Repository、ViewModel 的代码
User用户
@Entity(tableName = "users_table")
public class User {
@PrimaryKey(autoGenerate = true)
@NonNull
private int id;
@ColumnInfo(name = "name")
@NonNull
private String name;
@ColumnInfo(name = "profession")
@NonNull
private String profession;
public User(int id, @NonNull String name, @NonNull String profession) {
this.id = id;
this.name = name;
this.profession = profession;
}
all getters and setters also exist所有的 getter 和 setter 也都存在
UserDao用户道
@Dao
public interface UserDao {
@Query("SELECT * FROM users_table")
LiveData<List<User>> getAll();
@Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(User user);
@Delete
void delete(User user);
@Query("DELETE FROM users_table")
void deleteAll();
}
UserDatabase用户数据库
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class UserDatabase extends RoomDatabase {
public abstract UserDao userDao();
private static volatile UserDatabase INSTANCE;
private static final int NUM_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor
= Executors.newFixedThreadPool(NUM_OF_THREADS);
public static UserDatabase getDatabase(final Context context){
if (INSTANCE == null){
synchronized (UserDatabase.class){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
UserDatabase.class, "user.db")
.createFromAsset("users.db")
.build();
}
}
}
return INSTANCE;
}
}
UserRepository用户资料库
public class UserRepository {
private UserDao userDao;
private LiveData<List<User>> allUsers;
public UserRepository(Application application) {
UserDatabase db = UserDatabase.getDatabase(application);
userDao = db.userDao();
allUsers = userDao.getAll();
}
public LiveData<List<User>> getAllData() { return allUsers; }
public void insert(User user){
UserDatabase.databaseWriteExecutor.execute(() -> {
userDao.insert(user);
});
}
public void deleteAll(){
UserDatabase.databaseWriteExecutor.execute(() -> {
userDao.deleteAll();
});
}
}
UserViewModel用户视图模型
public class UserViewModel extends AndroidViewModel {
public static UserRepository repository;
public final LiveData<List<User>> allUsers;
public UserViewModel(@NonNull Application application) {
super(application);
repository = new UserRepository(application);
allUsers = repository.getAllData();
}
public LiveData<List<User>> getAllUsers() { return allUsers; }
public static void insert(User user) { repository.insert(user); }
}
This is only a guess that you are trying to check to see if the database exists before actually accessing the database.这只是一个猜测,您正试图在实际访问数据库之前检查数据库是否存在。 However, it's a relatively common issue.但是,这是一个相对普遍的问题。 It also assumes that you are using a compatible device (eg must be Android API 26+ for database inspector).它还假定您使用的是兼容设备(例如,对于数据库检查器,必须是 Android API 26+)。
When a database instance is retrieved, in your case, when in an activity/fragment using UserDatabase mydatabase = UserDatabase.getDatabase();
当检索数据库实例时,在您的情况下,当在活动/片段中使用UserDatabase mydatabase = UserDatabase.getDatabase();
does not actually open the database, rather it is only when an attempt is made to actually extract/insert/update/delete data that the database is opened, and in your case that the users.db file/asset is copied from the package to the final location on the device.实际上并没有打开数据库,而是只有在尝试实际提取/插入/更新/删除数据库打开的数据时,在您的情况下,users.db 文件/资产才从 package 复制到设备上的最终位置。 As such unless the attempt to access the database is made then neither Database Inspector nor Device File Explorer will show anything.因此,除非尝试访问数据库,否则数据库检查器和设备文件资源管理器都不会显示任何内容。
You can temporarily (or permanently) force an open by adding a line to the getDatabase
method in the UserDatabase
class. eg you could use:-您可以通过向UserDatabase
class 中的getDatabase
方法添加一行来临时(或永久)强制打开。例如,您可以使用:-
public abstract class UserDatabase extends RoomDatabase {
public abstract UserDao userDao();
private static volatile UserDatabase INSTANCE;
private static final int NUM_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor
= Executors.newFixedThreadPool(NUM_OF_THREADS);
public static UserDatabase getDatabase(final Context context){
if (INSTANCE == null){
synchronized (UserDatabase.class){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context,
UserDatabase.class, "user.db")
.allowMainThreadQueries() //NOTE ADDED for convenience of demo
.createFromAsset("users.db")
.build();
/*<<<<<<<<<< ADDED to FORCE an open of the database >>>>>>>>>>*/
SupportSQLiteDatabase sdb = INSTANCE.getOpenHelper().getWritableDatabase();
}
}
}
return INSTANCE;
}
}
Demonstration示范
The following is a demonstration.下面是一个演示。 It uses your code as posted in the question.它使用您在问题中发布的代码。 However with the following changes:-但是,进行了以下更改:-
User
class.已将 Getters 和 Setters 添加到User
class。context
rather than using the context to get the context (not an issue as only reason that a context is required is to get the default path to the database) . Room.databaseBuilder 的上下文使用传递的context
而不是使用上下文来获取上下文(这不是问题,因为需要上下文的唯一原因是获取数据库的默认路径) 。.allowMainThreadQueries
has been added to simplify the demo (as proof of the entire process).添加了.allowMainThreadQueries
以简化演示(作为整个过程的证明)。First a database was created using SQLite Studio, it was populated with 3 rows, as per:-首先使用 SQLite Studio 创建了一个数据库,其中填充了 3 行,如下所示:-
The Structure being:-结构是:-
The database file was copied (after closing the connection in SQLite Studio, opening the connection again and then closing it again to verify that the database was as it should be) (and renamed to users.db ) to the assets folder of the project (after creating the folder), as per:-将数据库文件复制(在SQLite Studio中关闭连接后,再次打开连接然后再次关闭以验证数据库是否正确) (并重命名为users.db )到项目的资产文件夹(创建文件夹后),按照:-
An activity MainActivity was used to demonstrate this at first being:-活动MainActivity最初用于证明这一点: -
public class MainActivity extends AppCompatActivity {
UserDatabase db;
UserDao dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = UserDatabase.getDatabase(this); // Does not open the database
dao = db.userDao(); // Does not open the database
}
}
Run 1运行 1
The above was run and from Android Studio the following was the result (noting the use of both Device File Explorer and Database Inspector):-运行上面的代码,从 Android Studio 得到以下结果(注意同时使用 Device File Explorer 和 Database Inspector):-
As can be seen nothing is shown in Database Inspector (App Inspection) nor in Device File Explorer after the App was successfully run.可以看出,在应用程序成功运行后,数据库检查器(应用程序检查)和设备文件资源管理器中都没有显示任何内容。
NOTE It can be seen that the getWritableDatabase line has been commented out (to demonstrate without).注意可以看到 getWritableDatabase 行已经被注释掉了(为了演示没有)。
Conclusion just getting an instance of the UserDatabase and getting an instance of the UserDao doe not result in the database being created.结论只是获取 UserDatabase 的实例和获取 UserDao 的实例不会导致创建数据库。
Run 2运行 2
This run demonstrates that accessing the database results in the database being created.此运行演示访问数据库会导致创建数据库。 It uses an non-live data version of the getAll query.它使用 getAll 查询的非实时数据版本。 In UserDao
the following was added:-在UserDao
中添加了以下内容:-
@Query("SELECT * FROM users_table")
List<User> getAllUsers();
Additionally MainActivity was changed to include (uncomment) the previously commented out code:-此外, MainActivity已更改为包括(取消注释)先前注释掉的代码:-
for (User user: dao.getAllUsers()) {
Log.d("USERINFO","User is " + user.getName() + " profession is " + user.getProfession());
}
Now when run:-现在运行时:-
and the log contains:-日志包含:-
D/USERINFO: User is Fred profession is Doctor
D/USERINFO: User is Mary profession is Solicitor
D/USERINFO: User is Jane profession is Vet
ie the data is as per the prepopulated database.即数据是按照预先填充的数据库。
Run 3运行 3
To demonstrate forcing the open in the getDatabase method the code that access the database in MainActivity was again commented out as per * Run 1 and in the UserDatabase
the commented out //SupportSQLiteDatabase sdb = INSTANCE.getOpenHelper().getWritableDatabase();
为了演示在 getDatabase 方法中强制打开,在MainActivity中访问数据库的代码再次按照 * Run 1注释掉,在UserDatabase
中注释掉//SupportSQLiteDatabase sdb = INSTANCE.getOpenHelper().getWritableDatabase();
was changed to be included an be SupportSQLiteDatabase sdb = INSTANCE.getOpenHelper().getWritableDatabase();
已更改为包含一个 be SupportSQLiteDatabase sdb = INSTANCE.getOpenHelper().getWritableDatabase();
AND IMPORTANTLY the App was uninstalled (once the database file exists it will not be copied from the assets folder again, so deleting the database (which uninstalling the App will do)) .重要的是,应用程序已卸载(一旦数据库文件存在,它将不会再次从资产文件夹中复制,因此删除数据库(卸载应用程序即可)) 。
No Asset File to be copied没有要复制的资产文件
After uninstalling the App and renaming the asset file to not_the_users.db
then the result is a crash/exception and the log includes:-卸载应用程序并将资产文件重命名为not_the_users.db
后,结果是崩溃/异常,日志包括:-
2021-08-16 08:12:11.884 26625-26625/a.a.so68791243javaroomnodatabase E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so68791243javaroomnodatabase, PID: 26625
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so68791243javaroomnodatabase/a.a.so68791243javaroomnodatabase.MainActivity}: java.lang.RuntimeException: Unable to copy database file.
Run 4 - Incompatible database运行 4 - 不兼容的数据库
For this run the users_table table was altered by changing the type of the profession column to STRING, copied into the assets folder as users.db and the App uninstalled.对于此运行,通过将专业列的类型更改为 STRING 来更改 users_table 表,将其作为 users.db 复制到资产文件夹中,并卸载应用程序。 The result, is the anticipated Expected/Found mismatch crash/exception as per:-结果是预期的 Expected/Found 不匹配崩溃/异常,如下所示:-
2021-08-16 08:30:42.523 27239-27239/a.a.so68791243javaroomnodatabase E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so68791243javaroomnodatabase, PID: 27239
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so68791243javaroomnodatabase/a.a.so68791243javaroomnodatabase.MainActivity}: java.lang.IllegalStateException: Pre-packaged database has an invalid schema: users_table(a.a.so68791243javaroomnodatabase.User).
Expected:
TableInfo{name='users_table', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, profession=Column{name='profession', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='users_table', columns={profession=Column{name='profession', type='STRING', affinity='1', notNull=true, primaryKeyPosition=0, defaultValue='null'}, name=Column{name='name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.