Android Studio Room:无法创建数据库文件
Android Studio Room: Unable to create database file
我正在尝试使用 Room(使用 Java)在 Android Studio 中创建一个简单的数据库。这是另一个应用程序中的插件,因此没有默认 Activity。当我转到 Device File Explorer
时,数据库文件不存在。想知道是否有人可以指出正确的方向?
这是我创建数据库的代码:
public abstract class AppDatabase extends RoomDatabase {
public abstract NoteDao NoteDao();
private static AppDatabase noteDB;
public static AppDatabase getInstance(Context context) {
if (null == noteDB) {
noteDB = buildDatabaseInstance(context);
}
return noteDB;
}
private static AppDatabase buildDatabaseInstance(Context context) {
return Room.databaseBuilder(context,
AppDatabase.class,
"exampleDB")
.allowMainThreadQueries().build();
}
public void cleanUp(){
noteDB = null;
}
}
这是我的 DAO 代码:
@Dao
public interface NoteDao {
@Query("SELECT * FROM note")
List<Note> getAll();
@Insert
void insert(Note note);
@Delete
void delete(Note note);
@Update
void update(Note note);
这是我的实体代码:
@Entity(tableName = "note")
public class Note {
@PrimaryKey(autoGenerate = true)
public int id;
@ColumnInfo(name = "name")
public String name;
public Note(String name){
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Note)) return false;
Note note = (Note) o;
if (id != note.id) return false;
return name != null ? name.equals(note.name) : note.name == null;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
我在插件入口点的 onCreate
方法中创建数据库。但是,当我使用下面的函数检查数据库是否存在时,它总是 returns False.
private static String doesDatabaseExist(Context context, String dbName) {
File dbFile = context.getDatabasePath(dbName);
String path = dbFile.getAbsolutePath();
return dbFile.exists();
}
当我进入 Device File Explorer
并搜索应该创建数据库文件的路径时,它不存在。它也不会出现在数据库检查器中。我浏览了很多教程,但没有发现我的代码有任何问题。 Gradle 文件和依赖项都是正确的(我遵循了 Android 开发人员指南)并且还在清单中添加了权限。
谢谢!
When I go into Device File Explorer and and search for the path where the database file should have been created, it does not exist.
可能的原因(无法肯定地说)是您没有访问数据库(例如,使用其中一个 dao 添加内容或尝试检索内容)。这是因为只有在尝试打开数据库时才真正创建数据库。
与其使用 Dao(例如 NoteDao.getAll()),不如使用(例如)强制打开:-
private static AppDatabase buildDatabaseInstance(Context context) {
AppDatabase db = Room.databaseBuilder(context,
AppDatabase.class,
"exampleDB")
.allowMainThreadQueries().build();
db.getOpenHelper().getWritableDatabase(); //<<<<< Forces an Open
return db;
}
也许考虑对您的代码进行以下调整,以便(最终)打开 2 个数据库(相同的名称以及它们是否被强制打开)。
已添加回调,以便在调用 onCreate 和 onOpen 时输出表示发生的事件已写入日志。
所以修改后的@Database class AppDatabase 是:-
@Database(entities = {Note.class},version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract NoteDao NoteDao();
private static AppDatabase noteDB;
public static final String TAG = "OPENINFO"; /* added */
/* not used for demo */
public static AppDatabase getInstance(Context context, String dbName, boolean forceOpen) {
if (null == noteDB) {
noteDB = buildDatabaseInstance(context,dbName,forceOpen);
}
return noteDB;
}
/* made public and added ability to pass dbname and also boolean to force open or not */
public static AppDatabase buildDatabaseInstance(Context context, String dbName, boolean forceOpen) {
AppDatabase db = Room.databaseBuilder(context,
AppDatabase.class,
dbName)
/* ADDED callbacks to allow open and create to be logged */
.addCallback(new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
Log.d(TAG,"OnCreate callback invoked for " + dbName);
}
@Override
public void onOpen(@NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
Log.d(TAG,"onOpen callback invoked for " + dbName);
}
@Override
public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
super.onDestructiveMigration(db);
}
})
.allowMainThreadQueries().build();
if (forceOpen) {
db.getOpenHelper().getWritableDatabase();
}
return db;
}
/* Placed in here to keep everything together */
/* Slightly modified and made public */
public static String doesDatabaseExist(Context context, String dbName) {
File dbFile = context.getDatabasePath(dbName);
String path = dbFile.getAbsolutePath();
return String.valueOf(dbFile.exists());
}
/* Will close DB if it is open */
public void cleanUp(){
if (noteDB != null && noteDB.isOpen()) {
noteDB.close();
}
noteDB = null;
}
}
在 activity 中使用以下代码:-
public class MainActivity extends AppCompatActivity {
AppDatabase db1, db2;
NoteDao dao1,dao2;
private static final String db1Name = "example1";
private static final String db2name = "example2";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* First run then neither file should exist at this stage, subsequent runs they will exist */
Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
Log.d(AppDatabase.TAG,"Getting instance for " + db1Name + " not force opened");
db1 = AppDatabase.buildDatabaseInstance(this,db1Name,false); /* not force open */
Log.d(AppDatabase.TAG,"Getting instance for " + db2name + " force opened");
db2 = AppDatabase.buildDatabaseInstance(this,db2name,true);
Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
dao1 = db1.NoteDao();
dao2 = db2.NoteDao();
Log.d(AppDatabase.TAG,"Dao retrieved for " + db1Name + " accessing db via Dao");
dao1.getAll(); /* will open DB */
Log.d(AppDatabase.TAG,"Dao retrieved for " + db2name + " accessing via Dao");
dao2.getAll(); /* would open DB BUT DB already opened */
Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
}
}
当第一个 运行 然后日志包括:-
2021-09-21 10:47:54.405 D/OPENINFO: File exists for example1 false
2021-09-21 10:47:54.405 D/OPENINFO: File exists for example2 false
2021-09-21 10:47:54.406 D/OPENINFO: Getting instance for example1 not force opened
2021-09-21 10:47:54.415 D/OPENINFO: Getting instance for example2 force opened
2021-09-21 10:47:54.443 D/OPENINFO: OnCreate callback invoked for example2
2021-09-21 10:47:54.450 D/OPENINFO: onOpen callback invoked for example2
2021-09-21 10:47:54.450 D/OPENINFO: File exists for example1 false
2021-09-21 10:47:54.450 D/OPENINFO: File exists for example2 true
2021-09-21 10:47:54.454 D/OPENINFO: Dao retrieved for example1 accessing db via Dao
2021-09-21 10:47:54.478 D/OPENINFO: OnCreate callback invoked for example1
2021-09-21 10:47:54.483 D/OPENINFO: onOpen callback invoked for example1
2021-09-21 10:47:54.487 D/OPENINFO: Dao retrieved for example2 accessing via Dao
2021-09-21 10:47:54.490 D/OPENINFO: File exists for example1 true
2021-09-21 10:47:54.490 D/OPENINFO: File exists for example2 true
可以看出,example2(强制打开)的文件在检索实例后存在,而 example1 在尝试通过 getAll()
.[=19 检索数据之前不存在=]
回调的输出确认相同。
从 DeviceExplorer 的角度来看,如果代码只是(仅第一个 运行):-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* First run then neither file should exist at this stage, subsequent runs they will exist */
Log.d(AppDatabase.TAG, "File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this, db1Name));
Log.d(AppDatabase.TAG, "File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this, db2name));
Log.d(AppDatabase.TAG, "Getting instance for " + db1Name + " not force opened");
db1 = AppDatabase.buildDatabaseInstance(this, db1Name, false); /* not force open */
Log.d(AppDatabase.TAG, "Getting instance for " + db2name + " force opened");
db2 = AppDatabase.buildDatabaseInstance(this, db2name, true);
Log.d(AppDatabase.TAG, "File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this, db1Name));
Log.d(AppDatabase.TAG, "File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this, db2name));
}
然后设备资源管理器显示:-
即已创建 example2 但未创建 example1
而如果来自 activity 的完整代码是 运行(仅第一个 运行)则:-
我认为您缺少数据库的注释。
添加,如下所示的这一行:
@Database(entities = Note.class,version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase appsDatabase;
public static synchronized AppDatabase getAppsDatabase(Context context){
if (appsDatabase == null){
appsDatabase = Room.databaseBuilder(context,
AppDatabase.class,
"notes_db").build();
}
return appsDatabase;
}
}
我正在尝试使用 Room(使用 Java)在 Android Studio 中创建一个简单的数据库。这是另一个应用程序中的插件,因此没有默认 Activity。当我转到 Device File Explorer
时,数据库文件不存在。想知道是否有人可以指出正确的方向?
这是我创建数据库的代码:
public abstract class AppDatabase extends RoomDatabase {
public abstract NoteDao NoteDao();
private static AppDatabase noteDB;
public static AppDatabase getInstance(Context context) {
if (null == noteDB) {
noteDB = buildDatabaseInstance(context);
}
return noteDB;
}
private static AppDatabase buildDatabaseInstance(Context context) {
return Room.databaseBuilder(context,
AppDatabase.class,
"exampleDB")
.allowMainThreadQueries().build();
}
public void cleanUp(){
noteDB = null;
}
}
这是我的 DAO 代码:
@Dao
public interface NoteDao {
@Query("SELECT * FROM note")
List<Note> getAll();
@Insert
void insert(Note note);
@Delete
void delete(Note note);
@Update
void update(Note note);
这是我的实体代码:
@Entity(tableName = "note")
public class Note {
@PrimaryKey(autoGenerate = true)
public int id;
@ColumnInfo(name = "name")
public String name;
public Note(String name){
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Note)) return false;
Note note = (Note) o;
if (id != note.id) return false;
return name != null ? name.equals(note.name) : note.name == null;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
我在插件入口点的 onCreate
方法中创建数据库。但是,当我使用下面的函数检查数据库是否存在时,它总是 returns False.
private static String doesDatabaseExist(Context context, String dbName) {
File dbFile = context.getDatabasePath(dbName);
String path = dbFile.getAbsolutePath();
return dbFile.exists();
}
当我进入 Device File Explorer
并搜索应该创建数据库文件的路径时,它不存在。它也不会出现在数据库检查器中。我浏览了很多教程,但没有发现我的代码有任何问题。 Gradle 文件和依赖项都是正确的(我遵循了 Android 开发人员指南)并且还在清单中添加了权限。
谢谢!
When I go into Device File Explorer and and search for the path where the database file should have been created, it does not exist.
可能的原因(无法肯定地说)是您没有访问数据库(例如,使用其中一个 dao 添加内容或尝试检索内容)。这是因为只有在尝试打开数据库时才真正创建数据库。
与其使用 Dao(例如 NoteDao.getAll()),不如使用(例如)强制打开:-
private static AppDatabase buildDatabaseInstance(Context context) {
AppDatabase db = Room.databaseBuilder(context,
AppDatabase.class,
"exampleDB")
.allowMainThreadQueries().build();
db.getOpenHelper().getWritableDatabase(); //<<<<< Forces an Open
return db;
}
也许考虑对您的代码进行以下调整,以便(最终)打开 2 个数据库(相同的名称以及它们是否被强制打开)。
已添加回调,以便在调用 onCreate 和 onOpen 时输出表示发生的事件已写入日志。
所以修改后的@Database class AppDatabase 是:-
@Database(entities = {Note.class},version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract NoteDao NoteDao();
private static AppDatabase noteDB;
public static final String TAG = "OPENINFO"; /* added */
/* not used for demo */
public static AppDatabase getInstance(Context context, String dbName, boolean forceOpen) {
if (null == noteDB) {
noteDB = buildDatabaseInstance(context,dbName,forceOpen);
}
return noteDB;
}
/* made public and added ability to pass dbname and also boolean to force open or not */
public static AppDatabase buildDatabaseInstance(Context context, String dbName, boolean forceOpen) {
AppDatabase db = Room.databaseBuilder(context,
AppDatabase.class,
dbName)
/* ADDED callbacks to allow open and create to be logged */
.addCallback(new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
Log.d(TAG,"OnCreate callback invoked for " + dbName);
}
@Override
public void onOpen(@NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
Log.d(TAG,"onOpen callback invoked for " + dbName);
}
@Override
public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
super.onDestructiveMigration(db);
}
})
.allowMainThreadQueries().build();
if (forceOpen) {
db.getOpenHelper().getWritableDatabase();
}
return db;
}
/* Placed in here to keep everything together */
/* Slightly modified and made public */
public static String doesDatabaseExist(Context context, String dbName) {
File dbFile = context.getDatabasePath(dbName);
String path = dbFile.getAbsolutePath();
return String.valueOf(dbFile.exists());
}
/* Will close DB if it is open */
public void cleanUp(){
if (noteDB != null && noteDB.isOpen()) {
noteDB.close();
}
noteDB = null;
}
}
在 activity 中使用以下代码:-
public class MainActivity extends AppCompatActivity {
AppDatabase db1, db2;
NoteDao dao1,dao2;
private static final String db1Name = "example1";
private static final String db2name = "example2";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* First run then neither file should exist at this stage, subsequent runs they will exist */
Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
Log.d(AppDatabase.TAG,"Getting instance for " + db1Name + " not force opened");
db1 = AppDatabase.buildDatabaseInstance(this,db1Name,false); /* not force open */
Log.d(AppDatabase.TAG,"Getting instance for " + db2name + " force opened");
db2 = AppDatabase.buildDatabaseInstance(this,db2name,true);
Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
dao1 = db1.NoteDao();
dao2 = db2.NoteDao();
Log.d(AppDatabase.TAG,"Dao retrieved for " + db1Name + " accessing db via Dao");
dao1.getAll(); /* will open DB */
Log.d(AppDatabase.TAG,"Dao retrieved for " + db2name + " accessing via Dao");
dao2.getAll(); /* would open DB BUT DB already opened */
Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
}
}
当第一个 运行 然后日志包括:-
2021-09-21 10:47:54.405 D/OPENINFO: File exists for example1 false
2021-09-21 10:47:54.405 D/OPENINFO: File exists for example2 false
2021-09-21 10:47:54.406 D/OPENINFO: Getting instance for example1 not force opened
2021-09-21 10:47:54.415 D/OPENINFO: Getting instance for example2 force opened
2021-09-21 10:47:54.443 D/OPENINFO: OnCreate callback invoked for example2
2021-09-21 10:47:54.450 D/OPENINFO: onOpen callback invoked for example2
2021-09-21 10:47:54.450 D/OPENINFO: File exists for example1 false
2021-09-21 10:47:54.450 D/OPENINFO: File exists for example2 true
2021-09-21 10:47:54.454 D/OPENINFO: Dao retrieved for example1 accessing db via Dao
2021-09-21 10:47:54.478 D/OPENINFO: OnCreate callback invoked for example1
2021-09-21 10:47:54.483 D/OPENINFO: onOpen callback invoked for example1
2021-09-21 10:47:54.487 D/OPENINFO: Dao retrieved for example2 accessing via Dao
2021-09-21 10:47:54.490 D/OPENINFO: File exists for example1 true
2021-09-21 10:47:54.490 D/OPENINFO: File exists for example2 true
可以看出,example2(强制打开)的文件在检索实例后存在,而 example1 在尝试通过 getAll()
.[=19 检索数据之前不存在=]
回调的输出确认相同。
从 DeviceExplorer 的角度来看,如果代码只是(仅第一个 运行):-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* First run then neither file should exist at this stage, subsequent runs they will exist */
Log.d(AppDatabase.TAG, "File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this, db1Name));
Log.d(AppDatabase.TAG, "File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this, db2name));
Log.d(AppDatabase.TAG, "Getting instance for " + db1Name + " not force opened");
db1 = AppDatabase.buildDatabaseInstance(this, db1Name, false); /* not force open */
Log.d(AppDatabase.TAG, "Getting instance for " + db2name + " force opened");
db2 = AppDatabase.buildDatabaseInstance(this, db2name, true);
Log.d(AppDatabase.TAG, "File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this, db1Name));
Log.d(AppDatabase.TAG, "File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this, db2name));
}
然后设备资源管理器显示:-
即已创建 example2 但未创建 example1
而如果来自 activity 的完整代码是 运行(仅第一个 运行)则:-
我认为您缺少数据库的注释。 添加,如下所示的这一行:
@Database(entities = Note.class,version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase appsDatabase;
public static synchronized AppDatabase getAppsDatabase(Context context){
if (appsDatabase == null){
appsDatabase = Room.databaseBuilder(context,
AppDatabase.class,
"notes_db").build();
}
return appsDatabase;
}
}