使用 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
)
State
和 Name
是枚举,定义如下:
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了。
我添加了一个数据 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
)
State
和 Name
是枚举,定义如下:
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=]
- 例如(简单)如果您想 select 所有以 A 开头的州,那么
引入所有额外数据的膨胀,从而导致效率低下。
由于多次存储相同的值而破坏规范化
数据库方式是table基于 CommandTile 合并 CommandTiles 和 UserPreferences 之间的 suitable 关系。
- 那么就不需要TypeConverter了。