Android 房间:根据外部输入创建 table

Android Room: Create table based on external input

我正在开发一个基于 Java 的 Android 应用程序,我正在使用 Room。该应用程序连接到服务器,从中下载项目特定配置。这些配置之一是 table 的设置。我有一个 table 每个项目的列数和类型不同。我需要在 phone 上有此 table 的本地副本以存储数据,以防没有可用的互联网连接。 table 的配置包含 table 的名称和列组成 like

[{
    "name":"column1",
    "datatype":"VARCHAR(20)"
},
{
    "name":"column2",
    "datatype":"INT(5)"
},
{
    "name":"column3",
    "datatype":"DOUBLE"
}]

如何使用 Room 生成这样的 table?生成创建查询不是问题,但我应该在哪里执行它。此外,如何从 table 插入、更新和查询数据?是否可以生成这样的 SQL 查询并执行它们?是否有类似行映射器的东西可用于从 table 读取查询数据? 如果这是不可能的,知道我该如何解决吗? 感谢您的支持。

您将无法使用 Room 执行此操作并使用 Room 的对象映射,因为 Room 根据对象映射构建 tables。

  • 那是一个table是根据一个用@Entity注解的class定义的,定义为一个实体到数据库。

它在编译时承担很多工作,例如验证构建其组件创建的查询 SQL。在 运行 时,作为打开数据库的一部分,它会检查预期的组件,如果发现差异,将 fail/crash.

我曾经有一个正在进行的项目,该项目基于数据库构建 entities/classes,可以将其复制到项目中,但会更改 Room 并引入功能(例如,忽略但随后引入的 DEFAULT 约束) .

您可以拥有不属于 controlled/known Room 的组件,包括 tables,这不会违反 运行 时间架构检查,但您必须使用支持SQLiteDatabase 所以你不妨使用原生 SQLite.

How can I generate such a table with Room?

在示例的情况下,您需要使用 @Entity 注释的 class。但是,Room 强加的一条规则是必须有一个 PRIMARY KEY。因此,一个选项可能是引入一个。让我们说一个名为 id

的列

Room 强制执行的另一条规则是列类型只能是 INTEGER、TEXT、REAL 或 BLOB。然而这是由变量类型决定的。

  1. 所以 VARCHAR(50) 将用于字符串,
  2. INT(5) 可能会存储 long 或 int long 会覆盖所有。
  3. DOUBLE 可能会存储一个 DOUBLE (REAL) 所以加倍。

所以 class(实体)可以是:-

@Entity
class TableX {
    @PrimaryKey
    Long id = null;
    String column1;
    long column2;
    double column3;
}
  • table 名称将是 TableX

How can I insert, update and query data from the table?

你使用一个接口或抽象 class 注释 @Dao,所以对于上面你可以,例如:-

@Dao
abstract class TableXDao {
    @Insert
    abstract long insert(TableX tableX);
    @Insert
    abstract long[] insert(TableX...tableX);
    @Query("INSERT INTO TableX (column1,column2,column3) VALUES(:column1,:column2,:column3)")
    abstract long insert(String column1,long column2, double column3);
    @Update
    abstract int update(TableX tableX);
    @Update
    abstract int update(TableX...tableX);
    @Query("UPDATE tablex set column1=:newColumn1, column2=:newColumn2,column3=:newColumn3 WHERE id=:id")
    abstract int update(long id, String newColumn1, long newColumn2, double newColumn3);
    @Query("SELECT * FROM tablex")
    abstract List<TableX> getAllTableXRows();
    
}
  • 注意 Insert/Update 的 3 种形式 @Insert/@Update 使用方便的方法(基于传递一个或多个对象)@Query 使用更多 free-format/adaptable 方法。

虽然没有要求 Room 需要知道数据库本身,所以另一个 class,用 @Database 注释是必需的,这个注释定义了构成数据库的实体,版本号(和其他选项) ). class 应该扩展 RoomDatabase class,它必须是抽象 class 或实现抽象方法 createOpenHelper(通常是前者)。所以:-

@Database(entities = {TableX.class},version = 1)
abstract class TheDatabase extends RoomDatabase {
    abstract TableXDao getTableXDao();
    
    /* Often :- */
    private static volatile TheDatabase instance = null;
    public TheDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(
                    context,TheDatabase.class,"thedatabase.db"
            )
                    .build();
        }
        return instance;
    }
}

当上面的编译很多时,构建会包含一个警告:-

    E:\AndroidStudioApps\SO70351715JavaSQLite\app\src\main\java\a\a\so70351715javasqlite\TheDatabase.java:10: warning: Schema export directory is not provided to the annotation processor so we cannot export the schema. You can either provide `room.schemaLocation` annotation processor argument OR set exportSchema to false.
abstract class TheDatabase extends RoomDatabase {
         ^
1 warning
  • 故意允许发生以演示广泛的编译时检查

此外,它会生成相当多的 java 代码 :-

TableXDao_Impl class是使用Dao时调用的代码 TheDatabase_Impl class 是访问数据库的代码,包括在 createAllTables 方法中创建 tables:-

  @Override
  public void createAllTables(SupportSQLiteDatabase _db) {
    _db.execSQL("CREATE TABLE IF NOT EXISTS `TableX` (`id` INTEGER, `column1` TEXT, `column2` INTEGER NOT NULL, `column3` REAL NOT NULL, PRIMARY KEY(`id`))");
    _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, '5f1c580621c8b86aef3b3cccc44d8d76')");
  }

如您所见,room_master_table 已创建并填充了存储哈希的行,这是验证的一部分,如果更改了哈希,则 room 将知道架构已更改(源代码已更改)。

Is there something like a row mapper which can be used to read the queried data from the table?

可以看出,这一切都是通过注释通过编译代码完成的,因此没有映射,但期望在编译时一切都是 known/defined。

If this is not possible, any idea how I can solve it otherwise? Use Native SQLite or manage the server database and the room database as a whole.