使用 Enum 计算数据库实时数据时出现异常

Exception while computing database live data with Enum

我添加了一个数据 class 并尝试将其保存到 Room 中。我浏览了 Whosebug 并没有找到答案。

所以,错误是:

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $

我正在使用 Room 2.4.2,因此应该支持枚举。

我使用的模型是:

@Entity(tableName = "userpreferencestable")
class UserPreferencesEntity (
    @PrimaryKey()
    var vin: String,
    @ColumnInfo(name = "control")
    var command: List<CommandTile?>
)

CommandTile定义如下:

data class CommandTile(
    @SerializedName("name")
    var name: DashboardTile.Name,
    @SerializedName("state")
    var state: DashboardTile.State
)

StateName 是枚举,定义如下:

   enum class Name {
       NAME1,
       NAME2...
    }

    enum class State {
        TAT,
        TOT
    }

我尝试添加 DataConverter,但它不起作用。

 @TypeConverter
    fun fromName(name: Name): String {
        return name.name
    }

    @TypeConverter
    fun toName(name: String): Name {
        return Name.valueOf(name)
    }

    @TypeConverter
    fun fromState(state: State): String {
        return state.name
    }

    @TypeConverter
    fun toState(state: String):State {
        return State.valueOf(state)
    }


还是不行。我不知道如何用枚举保存数据列表 class。

有什么想法吗?

要转​​换枚举值,您必须这样做

       @TypeConverter
        fun fromName(name: Name): String {
            return name.name
        }
    
        @TypeConverter
        fun toName(name: String): Name {
            return enumValueOf<Name>(name)
        }
    
      

您的问题不是枚举,而是 command List<CommandTile>根据公开的代码)。

TypeConverter 用于将数据的 from/to 列转换为 stored/retrieved。

因为您没有 CommandTile class 的 @Entity 注释,而是将 List<CommandTile?> 作为 [=74= 中的一列]UserPrefrences class, 确实有@Entity注解,那么Room会想要转换CommandTile[=的列表104=]s 到 acceptable 类型(在 SQLite 中以及 Room 的限制,这必须是解析为 TEXT (String) 之一的类型, INTEGER (Int, Long ...), REAL (Double, Float ....) 或 BLOB (ByteArray).

  • 括号中的类型是 Kotlin 类型,它们是示例,并不全面。

例如,要克服使用 List 时可能遇到的问题,请考虑以下内容:-

一个新的classCommandTileList

data class CommandTileList(
    val commandTileList: List<CommandTile>
)
  • 避免列是列表

修改了 UserPreferencesEntity class 以使用 CommandTileList 而不是 List<CommandTile>

@Entity(tableName = "userpreferencestable")
class UserPreferencesEntity (
    @PrimaryKey()
    var vin: String,
    @ColumnInfo(name = "control")
    var command: CommandTileList
)

TypeConvertersclass,带有适当的 TypeConverters

class TypeConverters {

    @TypeConverter
    fun fromCommandTileToString(commandTileList: CommandTileList): String {
        return Gson().toJson(commandTileList)
    }
    @TypeConverter
    fun fromStringToCommandTile(string: String): CommandTileList {
        return Gson().fromJson(string,CommandTileList::class.java)
    }
}

A suitable@Dao注解classAllDAO(演示用)

@Dao
interface AllDAO {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(userPreferencesEntity: UserPreferencesEntity): Long
    @Query("SELECT * FROM userpreferencestable")
    fun getAllUserPreferences(): List<UserPreferencesEntity>
}

A suitable @Database 注释 class TheDatabase (用于演示)注意到 TypeConverters class 通过@TypeConverters 注解(不是复数形式而是单数形式

@TypeConverters(value = [TypeConverters::class])
@Database(entities = [UserPreferencesEntity::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDAO(): AllDAO

    companion object {
        var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}
  • .allowMainThreadQueries convenience/brevity

终于在 Activty MainActivity

中付诸行动
class MainActivity : AppCompatActivity() {

    lateinit var db: TheDatabase
    lateinit var dao: AllDAO
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val c1 = CommandTile(DashboardTile.Name.NAME1,DashboardTile.State.TAT)
        val c2 = CommandTile(DashboardTile.Name.NAME2,DashboardTile.State.TOT)

        db = TheDatabase.getInstance(this)
        dao = db.getAllDAO()
        dao.insert(userPreferencesEntity = UserPreferencesEntity("VIN1", CommandTileList(listOf(
        c1,c2))))
        for(u in dao.getAllUserPreferences()) {
            Log.d("DBINFO","VIV = ${u.vin} CommandTiles in Command = ${u.command.commandTileList.size} They Are:-")
            for (ct in u.command.commandTileList) {
                Log.d("DBINFO","\tName = ${ct.name} State = ${ct.state}")
            }
        }
    }
}

结果

日志包括:-

D/DBINFO: VIV = VIN1 CommandTiles in Command = 2 They Are:-
D/DBINFO:   Name = NAME1 State = TAT
D/DBINFO:   Name = NAME2 State = TOT

数据库,通过 App Inspection :-

如您所见,2 个 CommandTiles 的列表(一个 CommandTileList)已转换为字符串(SQLite 类型 TEXT)并存储并随后检索。

注意从数据库的角度来看这并不理想呢

  • limits/complicates存储数据的有用性。

    • 例如(简单)如果您想 select 所有以 A 开头的州,那么 SELECT * FROM userpreferencestable WHERE command LIKE 'A%' 会找到您必须使用 SELECT * FROM userpreferencestable WHERE 'state":A%'.[= 之类的所有列112=]
  • 引入所有额外数据的膨胀,从而导致效率低下。

  • 由于多次存储相同的值而破坏规范化

数据库方式是table基于 CommandTile 合并 CommandTiles 和 UserPreferences 之间的 suitable 关系。

  • 那么就不需要TypeConverter了。