如何在 Room 中插入一组可为空的实体?

How to insert a set of nullable entites in Room?

我有一个名为 City 的房间实体:

@Entity(tableName = "cities")
class City(

    @PrimaryKey
    @ColumnInfo(name = "unique_city_id")
    val id: Long,

    @ColumnInfo(name = "city_name")
    val name: String,

    @ColumnInfo(name = "city_code")
    val code: String,
)

我有一个 class 类型的对象列表:

data class CoffeeHouse(
    override val id: Long,
    override val latitude: Double,
    override val longitude: Double,
    override val city: City?,
    override val address: String,
)

我需要保存 CoffeeHouse 和 City classes。因为有很多相同的城市,所以我将咖啡馆列表映射到一组城市以仅获得唯一的城市:

val cities = coffeeHouses.map { it.city?.toPersistenceType() }.toSet()

(.toPersistenceType() 只是将域类型映射到持久性)

然后我使用这些 DAO 将 coffeeHousescities 插入到 Room 数据库中:

@Dao
abstract class CoffeeHouseDao(val cacheDatabase: CacheDatabase) {

    private val cityDao = cacheDatabase.cityDao()

    @Insert(onConflict = REPLACE)
       abstract suspend fun insertAllCoffeeHouses(coffeeHouses: List<CoffeeHouse>)

    @Transaction
        open suspend fun insertAllCoffeeHousesInfo(
           coffeeHouses: List<CoffeeHouse>,
           cities: Set<City?>,
        ) {
           insertAllCoffeeHouses(coffeeHouses)
           cityDao.setCities(cities)
        }
}

@Dao
interface CityDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun setCities(cities: Set<City?>)

问题是当我尝试插入 Set 应用时出现异常崩溃:

Uncaught exception java.lang.NullPointerException: 
Attempt to invoke virtual method 'long com.coffeeapp.android.persistence.entity.City.getId()' 
on a null object reference

stacktrace点在cities插入的那一行,所以我不明白如何正确。

发生这种情况是因为您已将 city 中的 ID 字段设置为该 table 的主键,并且它不能为空。

您可以尝试将注释更改为

@PrimaryKey(autoGenerate = true)

或者,如果您不想自动递增,则必须确保在插入城市时 ID 不为空。

我认为是因为 CofeeHouse 实体中的 city: City?cities: Set 。尝试使它们不可为空。

要允许插入带有 null 的 a,您可以使用 :-

@Entity(tableName = "cities")
class City(

    @PrimaryKey
    @ColumnInfo(name = "unique_city_id")
    val id: Long?, //<<<<<<<< ? ADDED

    @ColumnInfo(name = "city_name")
    val name: String,

    @ColumnInfo(name = "city_code")
    val code: String,
)

这样插入时会生成id。例如以下(为方便起见,基于减少)。

但是,REPLACE 冲突策略永远不会导致替换,因为 null 会生成唯一的 id.

我相信你想要的是城市名称、城市代码或两者(一起或独立)构成一个唯一的条目。

因此 :-

@Entity(
    tableName = "cities",
    indices = [
        /*
            probably just one of these all three is overkill
         */
        Index(value = ["city_name"],unique = true),
        Index(value = ["city_code"], unique = true),
        Index(value = ["city_name","city_code"],unique = true)
    ]
)
class City(

    @PrimaryKey
    @ColumnInfo(name = "unique_city_id")
    val id: Long?,

    @ColumnInfo(name = "city_name")
    val name: String,

    @ColumnInfo(name = "city_code")
    val code: String,
)

例如考虑以下内容:-

    cityDao.setCities(setOf<City>(City(null,"Sydney","SYD1"),City(null,"New York","NY1")))
    cityDao.setCities(setOf<City>(City(null,"Sydney","SYD1"),City(null,"New York","NY1")))
  • 因此尝试添加同一组城市结果是:-

即第一次添加 id 为 1 和 2 的悉尼和纽约,第二次尝试由于删除原件的冲突而被替换,所以你最终得到 id 的 3 和 4。如果没有唯一索引,那么结果将是 4 行ID 为 1、2、3 和 4。