通常,这可以通过将外部数据库作为资产(预打包数据库)并利用
.createFromAsset
来进行,该方法将执行数据库的复制。
然而,使用上述方法
需要资产数据库严格符合通过
@Entity
注释的类定义的 room 模式,并包含在通过
@Database
注释提供的实体列表/数组中。
可以使用
prePackagedDatabaseCallback
修改预打包的数据库以适应。但是,更简单的方法可能是对外部数据库进行更改,使其与 room 期望的相匹配。
例如,在您的情况下,外部/预打包的数据库中的
profile 表存在不匹配,因为您将
id 设置为(有效地)
INTEGER PRIMARY KEY
,而使用
@PrimaryKey(autoGenerate = true)
,room 期望
INTEGER PRIMARY KEY AUTOINCREMENT
。
AUTOINCREMENT
(在 room 中 autogenerate = true
)效率低下且很少需要。您可以更改为使用 @PrimaryKey
。
让 Room 让它变得简单
只讨论了一个不匹配的示例,因为很可能没有必要深入研究所有会导致问题的不匹配。当创建并包含在
@Database
注释的类列表中的带有
@Entity
注释的类时,编译项目时 room 将生成预期的
CREATE TABLE ....
语句。因此,您可以根据 Room 创建的 SQL 轻松地对外部/预打包的表进行所需的更改。
Room 创建的 SQL 可在 java(生成)中找到(在 Android View 中可见),要查找的类与用
@Database
注释注释的类同名,但后缀为 _Impl。 SQL 可在名为
createAllTables
的方法中找到。您不想创建 room_master_table,这是 room 用于存储模式的哈希表示形式并用于检查是否已对模式进行更改的表。
演示
使用与您的问题相同的代码/模式(通过一些微小的更改简化演示)。
第1步-根据您的模式预打包数据库
首先,根据您的 SQL 创建了一个数据库,并使用第三方工具(Navicat)填充了大约100个配置文件和10000本书。
DROP TABLE IF EXISTS "profile_table";
DROP TABLE IF EXISTS "books_table";
CREATE TABLE IF NOT EXISTS "profile_table" ( "id" INTEGER, "profile_name" TEXT, PRIMARY KEY("id") );
CREATE TABLE "books_table" ( "book_id" INTEGER, "book_name" TEXT, "all" INTEGER, "profile_id" INTEGER, PRIMARY KEY("book_id" AUTOINCREMENT) );
WITH loop(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM loop LIMIT 100)
INSERT INTO profile_table (profile_name) SELECT 'Profile'||x FROM loop;
WITH loop(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM loop LIMIT 10000)
INSERT INTO books_table (book_name,`all`,profile_id) SELECT 'Book'||x,abs(random() % 10),(abs(random()) % (SELECT max(id) FROM profile_table)) + 1 FROM loop;
SELECT * FROM books_table JOIN profile_table ON profile_id=id ORDER BY CAST(substr(profile_name,8) AS INTEGER);
展示数据的查询如下:
![enter image description here](https://istack.dev59.com/nCXFm.webp)
- 实际上显示了10000行
步骤2 - 创建资产
创建了assets目录,并将数据库复制并粘贴为student_database.db :-
![enter image description here](https://istack.dev59.com/zmMFo.webp)
步骤3 添加createFromAsset
和其他更改以运行初始测试
MyRoomDatabase类已更改以添加.createFromAsset
:-
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
MyRoomDatabase.class, "student_prepare_room_database")
.createFromAsset("student_database.db") //<<<<< STEP 3 - ADDED
.addCallback(sRoomDatabaseCallback) //<<<< added for convenience/brevity
.allowMainThreadQueries()
.build();
}
此外,
BookDAO 已被修改,新增了以下内容:
@Query("SELECT * FROM books_table")
List<Book> getAllBooks();
- 允许简单地访问数据库(除非访问否则不会打开、检查等)。
最后,MainActivity 的编码如下:
public class MainActivity extends AppCompatActivity {
MyRoomDatabase db;
BookDAO bookDAO;
ProfileDAO profileDAO;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = MyRoomDatabase.getDatabase(this);
bookDAO = db.bookDao();
profileDAO = db.profileDAO();
for (Book b: bookDAO.getAllBooks()) {
Log.d("BOOKINFO","Book is " + b.getName());
}
}
}
第四步 房间SQL
项目随后被编译(添加了getter/setter),createALLTables如下:
![enter image description here](https://istack.dev59.com/qd2no.webp)
- 请注意profile表上的AUTOINCREMENT。
第五步 运行(见注释)
- 注释:这一步仅是为了演示如果数据库未被修改会发生什么。
以上所有内容,如果应用程序运行(尽管不应该运行,因为它会失败),则会像预期的那样失败,并出现“Expected/Found”不匹配的问题,但很明显它已经复制了资源文件:
2022-01-05 07:59:02.200 1941-1941/? D/AndroidRuntime: Shutting down VM
2022-01-05 07:59:02.202 1941-1941/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so70580333prepackageddatabase, PID: 1941
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so70580333prepackageddatabase/a.a.so70580333prepackageddatabase.MainActivity}: java.lang.IllegalStateException: Pre-packaged database has an invalid schema: books_table(a.a.so70580333prepackageddatabase.Book).
Expected:
TableInfo{name='books_table', columns={all=Column{name='all', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, book_id=Column{name='book_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, book_name=Column{name='book_name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, profile_id=Column{name='profile_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='books_table', columns={all=Column{name='all', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, profile_id=Column{name='profile_id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, book_id=Column{name='book_id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, book_name=Column{name='book_name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
步骤6 修改预打包数据库
重新访问第三方工具,并使用以下内容:
DROP TABLE IF EXISTS books_table_original;
DROP TABLE IF EXISTS profile_table_original;
ALTER TABLE books_table RENAME TO books_table_original;
ALTER TABLE profile_table RENAME TO profile_table_original;
CREATE TABLE IF NOT EXISTS `books_table` (`book_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `book_name` TEXT, `all` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL);
CREATE TABLE IF NOT EXISTS `profiles_table` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `profile_name` TEXT);
INSERT INTO profiles_table SELECT * FROM profile_table_original;
INSERT INTO books_table SELECT * FROM books_table_original;
DROP TABLE IF EXISTS books_table_original;
DROP TABLE IF EXISTS profile_table_original;
VACUUM;
运行以上操作后,将保存数据库并将其复制到资产文件夹。
![enter image description here](https://istack.dev59.com/Cs9pc.webp)
- 在这种情况下,数据库最初被粘贴为AlteredFroRoom_student_database.db,然后删除了student_database.db,然后将AlterForRoom_student_database.db复制并粘贴为student_database.db。注意对于要分发的应用程序,您不希望有额外的数据库副本,因为这会增加分发的大小。
然后卸载该应用程序并重新运行,它将成功运行。
- 请注意,由于已复制数据库,因此不会调用onCreate回调(将调用onOpen回调)。
日志包括:
2022-01-05 08:56:15.975 D/BOOKINFO: Book is Book1
2022-01-05 08:56:15.975 D/BOOKINFO: Book is Book2
2022-01-05 08:56:15.975 D/BOOKINFO: Book is Book3
2022-01-05 08:56:15.975 D/BOOKINFO: Book is Book4
2022-01-05 08:56:15.975 D/BOOKINFO: Book is Book5
2022-01-05 08:56:15.975 D/BOOKINFO: Book is Book6
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book7
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book8
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book9
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book10
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book11
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book12
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book13
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book14
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book15
2022-01-05 08:56:15.976 D/BOOKINFO: Book is Book16
....