您是否在每次进行更改时重新复制资产文件并卸载应用程序?如果没有,这并不能解决问题,那么:-
如果您编译房间代码,然后查看 your_database_class_impl 中的 java(generated)(其中 your_database_class 是您的 @Database 类名)在 createAllTables 方法中。这有用于创建数据库的 SQL(您可以忽略 room_master SQL)。
例如(这基于您的实体和最小引用表):-
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `Authors` (`AuthorID` INTEGER, `AuthorName` TEXT, PRIMARY KEY(`AuthorID`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS `Sources` (`SourceID` INTEGER, `SourceName` TEXT, PRIMARY KEY(`SourceID`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS `Author_By_Source` (`AuthorID` INTEGER NOT NULL, `SourceID` INTEGER NOT NULL, PRIMARY KEY(`AuthorID`, `SourceID`), FOREIGN KEY(`AuthorID`) REFERENCES `Authors`(`AuthorID`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`SourceID`) REFERENCES `Sources`(`SourceID`) ON UPDATE NO ACTION ON DELETE NO ACTION )");
_db.execSQL("CREATE INDEX IF NOT EXISTS `index_Author_By_Source_AuthorID` ON `Author_By_Source` (`AuthorID`)");
_db.execSQL("CREATE INDEX IF NOT EXISTS `index_Author_By_Source_SourceID` ON `Author_By_Source` (`SourceID`)");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '953f8512db886fb7d206fa561a7117c4')");
}
您需要相应地转换数据库,即按照定义,因此例如您可以在 SQLite Studio 中针对上述情况使用
/* Just once */
PRAGMA foreign_keys = 0;
/* Per table */
DROP TABLE IF EXISTS Author_By_Source_new;
DROP TABLE IF EXISTS Author_By_Source_original;
CREATE TABLE IF NOT EXISTS Author_By_Source_new copy_generated_sql_for_the_column_definitions_etc;
DELETE FROM Author_By_Source_new;
INSERT INTO Author_By_Source_new SELECT * FROM Author_By_Source;
ALTER TABLE Author_By_Source RENAME TO Author_By_Source_original;
ALTER TABLE Author_By_Source_new RENAME TO Author_By_Source;
/* IF HAPPY THEN DO */
-- DROP TABLE IF EXISTS Author_By_Source_original; /* NOTE TURNED OFF */
...... do the equivalent for all tables
/* Just once */
PRAGMA foreign_keys = 1;
PRAGMA foreign_key_check;
PRAGMA integrity_check;
示例
这是一个基于您的代码可用内容的示例。
原始预填充数据库
使用 SQLite 管理工具 (Navicat) 生成数据库:-
DROP TABLE IF EXISTS Author_By_Source;
DROP TABLE IF EXISTS Authors;
DROP TABLE IF EXISTS Sources;
CREATE TABLE IF NOT EXISTS Authors (AuthorID INTEGER PRIMARY KEY, AuthorName TEXT);
CREATE TABLE IF NOT EXISTS Sources (SourceID INTEGER PRIMARY KEY, SourceName TEXT);
/* Create the table so that it would cause issues in Room */
CREATE TABLE IF NOT EXISTS Author_By_Source (
AuthorId INTEGER, SourceId INTEGER,
FOREIGN KEY (AuthorID) REFERENCES Authors(AuthorID),
FOREIGN KEY (SourceID) REFERENCES Sources(SourceID)
);
INSERT INTO Authors (AuthorName) VALUES ('Fred'),('Mary'),('Joan'),('Bert'),('Alan');
INSERT INTO Sources (SourceName) VALUES ('S1'),('S2'),('S3'),('S4'),('S5');
INSERT INTO Author_By_Source VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(1,4),(3,4),(5,2);
SELECT AuthorName, SourceName
FROM Author_By_Source
JOIN Authors ON Author_By_Source.AuthorID = Authors.AuthorID
JOIN Sources ON Author_By_Source.SourceID = Sources.SourceID
;
运行后:-
AuthorName SourceName
Fred S1
Mary S2
Joan S3
Bert S4
Alan S5
Fred S4
Joan S4
Alan S2
正如预期的那样,尝试使用它会导致:-
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so59756782javaroomprepopulatedconversion/a.a.so59756782javaroomprepopulatedconversion.MainActivity}: java.lang.IllegalStateException: Pre-packaged database has an invalid schema: Authors(a.a.so59756782javaroomprepopulatedconversion.Authors).
Expected:
TableInfo{name='Authors', columns={AuthorName=Column{name='AuthorName', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, AuthorID=Column{name='AuthorID', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='Authors', columns={AuthorID=Column{name='AuthorID', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, AuthorName=Column{name='AuthorName', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
转换
根据答案,以下内容用于转换数据库:-
BEGIN TRANSACTION;
DROP TABLE IF EXISTS Author_By_Source;
DROP TABLE IF EXISTS Authors;
DROP TABLE IF EXISTS Sources;
CREATE TABLE IF NOT EXISTS `Authors` (`AuthorID` INTEGER NOT NULL, `AuthorName` TEXT, PRIMARY KEY(`AuthorID`));
CREATE TABLE IF NOT EXISTS `Sources` (`SourceID` INTEGER NOT NULL, `SourceName` TEXT, PRIMARY KEY(`SourceID`));
/* Create the table so that it would cause issues in Room */
CREATE TABLE IF NOT EXISTS Author_By_Source (
AuthorId INTEGER, SourceId INTEGER,
FOREIGN KEY (AuthorID) REFERENCES Authors(AuthorID),
FOREIGN KEY (SourceID) REFERENCES Sources(SourceID)
);
INSERT INTO Authors (AuthorName) VALUES ('Fred'),('Mary'),('Joan'),('Bert'),('Alan');
INSERT INTO Sources (SourceName) VALUES ('S1'),('S2'),('S3'),('S4'),('S5');
INSERT INTO Author_By_Source VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(1,4),(3,4),(5,2);
SELECT AuthorName, SourceName
FROM Author_By_Source
JOIN Authors ON Author_By_Source.AuthorID = Authors.AuthorID
JOIN Sources ON Author_By_Source.SourceID = Sources.SourceID
;
/* Just once */
PRAGMA foreign_keys = 0;
/* Per table */
DROP TABLE IF EXISTS Author_By_Source_new;
DROP TABLE IF EXISTS Author_By_Source_original;
CREATE TABLE IF NOT EXISTS `Author_By_Source_new`
(
`AuthorID` INTEGER NOT NULL,
`SourceID` INTEGER NOT NULL,
PRIMARY KEY(`AuthorID`, `SourceID`),
FOREIGN KEY(`AuthorID`) REFERENCES `Authors`(`AuthorID`) ON UPDATE NO ACTION ON DELETE NO ACTION ,
FOREIGN KEY(`SourceID`) REFERENCES `Sources`(`SourceID`) ON UPDATE NO ACTION ON DELETE NO ACTION
);
DELETE FROM Author_By_Source_new;
INSERT INTO Author_By_Source_new SELECT * FROM Author_By_Source;
ALTER TABLE Author_By_Source RENAME TO Author_By_Source_original;
ALTER TABLE Author_By_Source_new RENAME TO Author_By_Source;
/* IF HAPPY THEN DO */
DROP TABLE IF EXISTS Author_By_Source_original;
-- ...... do the equivalent for all tables
CREATE INDEX IF NOT EXISTS `index_Author_By_Source_SourceID` ON `Author_By_Source` (`SourceID`);
CREATE INDEX IF NOT EXISTS `index_Author_By_Source_AuthorID` ON `Author_By_Source` (`AuthorID`);
COMMIT;
/* Just once */
PRAGMA foreign_keys = 1;
PRAGMA foreign_key_check;
PRAGMA integrity_check;
SELECT sql FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite%';
SELECT AuthorName, SourceName
FROM Author_By_Source
JOIN Authors ON Author_By_Source.AuthorID = Authors.AuthorID
JOIN Sources ON Author_By_Source.SourceID = Sources.SourceID
;
数据库已关闭,时间戳和大小检查,然后数据库文件替换了资产文件夹中的现有文件。该应用程序已卸载,然后在 MainActivity 中使用以下内容重新运行:-
myDB = Room.databaseBuilder(this,MyAppDatabase.class,"mydb")
.createFromAsset("mydb")
.allowMainThreadQueries()
.build();
myDB.getOpenHelper().getWritableDatabase();
List<AuthorWithSources> authorWithSourcesList = myDB.allDao().getAllAuthorsWithSources();
for (AuthorWithSources aws: authorWithSourcesList) {
Log.d("AUTHORSOURCEINFO","Author = " + aws.authors.getAuthorName() + "Source = " + aws.sources.getSourceName());
}
}
AuthorWithSources 是:-
public class AuthorWithSources {
@Embedded
AuthorBySource authorBySource;
@Relation(entity = Authors.class,parentColumn = "AuthorID",entityColumn = "AuthorID")
Authors authors;
@Relation(entity = Sources.class,parentColumn = "SourceID",entityColumn = "SourceID")
Sources sources;
}
结果
没有错误并且在日志中:-
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = FredSource = S1
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = MarySource = S2
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = JoanSource = S3
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = BertSource = S4
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = AlanSource = S5
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = FredSource = S4
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = JoanSource = S4
2020-01-16 10:09:23.623 D/AUTHORSOURCEINFO: Author = AlanSource = S2
替代方法
存在一个工具,作为一个应用程序,可以转换数据库以供 Room 使用,并为实体/道生成 java 代码。
这里有详细的工具RoomExistingSQLiteDBConverter
使用工具和
- 将生成的java源代码复制到项目中,然后
- 访问每个文件并解析导入(假设输入了正确的包名称,否则还必须调整或添加包)然后
- 在项目中创建assets文件夹,然后
- 复制生成的数据库
-
最后创建代码以使用数据库,例如
soanswersDatabase = Room.databaseBuilder(this,SoanswersDatabase.class,SoanswersDatabase.DBNAME)
.allowMainThreadQueries()
.createFromAsset(SoanswersDatabase.DBNAME)
.build();
List<Author_By_Source> authorBySourceList = soanswersDatabase.getAuthor_By_SourceDao().getEveryAuthor_By_Source();
for (Author_By_Source abs: authorBySourceList) {
Log.d("AUTHORSOURCEINFO",
"AuthorReference = " + String.valueOf(abs.getAuthorId())
+ " Sourcereference = " + String.valueOf(abs.getSourceId())
);
}
- 即上面是唯一输入的代码。
结果:-
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 1 Sourcereference = 1
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 2 Sourcereference = 2
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 3 Sourcereference = 3
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 4 Sourcereference = 4
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 5 Sourcereference = 5
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 1 Sourcereference = 4
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 3 Sourcereference = 4
2020-01-16 10:46:27.907 D/AUTHORSOURCEINFO: AuthorReference = 5 Sourcereference = 2