You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移至Kotlin Multiplatform后Room部分DAO插入方法未解析问题

Room迁移Kotlin Multiplatform后内部任务类中DAO未解析问题

问题描述

将应用迁移至Kotlin Multiplatform,使用Room版本2.7.0-alpha05。Room相关的@Database、@Entity、@DAO注解代码转换完成且无报错,19个实体的插入操作编译正常,但有两个插入函数的内部任务类call方法中出现DAO未解析错误——这两个DAO变量在MainViewModel的其他查询、删除操作中可正常识别。

相关代码

DAO定义

@Dao
interface CustomGeofencePerimeterMarkerTableDao{
    @Insert
    fun insertCustomGeofencePerimeterMarkerTable(customGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable): Long?

    @Query("DELETE FROM CustomGeofencePerimeterMarkerTable WHERE customGeofencePerimeterMarkerTablePrimaryKey = :customGeofencePerimeterMarkerTablePrimaryKey")
    fun deleteCustomGeofencePerimeterMarkerTable(customGeofencePerimeterMarkerTablePrimaryKey: Long)

    @Query("DELETE FROM CustomGeofencePerimeterMarkerTable")
    fun deleteAllCustomGeofencePerimeterMarkerTables()

    @Query("SELECT * FROM CustomGeofencePerimeterMarkerTable")
    fun findAllCustomGeofencePerimeterMarker(): MutableList<CustomGeofencePerimeterMarkerTable?>?

    @Query("SELECT * FROM CustomGeofencePerimeterMarkerTable WHERE customGeofencePerimeterMarkerContactAddressTableFirebaseKey = :customGeofencePerimeterMarkerContactAddressTableFirebaseKey")
    fun findAllCustomGeofencePerimeterMarkerByContactAddressFirebaseKey(
        customGeofencePerimeterMarkerContactAddressTableFirebaseKey: String?
    ): CustomGeofencePerimeterMarkerTable?

    @Query("SELECT * FROM CustomGeofencePerimeterMarkerTable WHERE customGeofencePerimeterMarkerTableFirebaseKey = :customGeofencePerimeterMarkerTableFirebaseKey")
    fun findCustomGeofencePerimeterMarkerTableByFirebaseKey(
        customGeofencePerimeterMarkerTableFirebaseKey: String?
    ): CustomGeofencePerimeterMarkerTable?

    @Query(
        "UPDATE CustomGeofencePerimeterMarkerTable SET " +
                "customGeofencePerimeterMarkerTablePrimaryKey = :customGeofencePerimeterMarkerTablePrimaryKey, " +
                "customGeofencePerimeterMarkerTableFirebaseKey = :customGeofencePerimeterMarkerTableFirebaseKey, " +
                "customGeofencePerimeterMarkerContactAddressTableFirebaseKey = :customGeofencePerimeterMarkerContactAddressTableFirebaseKey, " +
                "customGeofencePerimeterMarkerDateAndTime = :customGeofencePerimeterMarkerDateAndTime, " +
                "customGeofencePerimeterMarkerString = :customGeofencePerimeterMarkerString " +
                "WHERE customGeofencePerimeterMarkerTablePrimaryKey = :customGeofencePerimeterMarkerTablePrimaryKey"
    )
    fun updateCustomGeofencePerimeterMarkerTable(
        customGeofencePerimeterMarkerTablePrimaryKey: Long,
        customGeofencePerimeterMarkerTableFirebaseKey: String?,
        customGeofencePerimeterMarkerContactAddressTableFirebaseKey: String?,
        customGeofencePerimeterMarkerDateAndTime: String?,
        customGeofencePerimeterMarkerString: String?
    )
}

@Dao
interface CurrentLocationTableDao{
    @Insert
    fun insertCurrentLocation(currentLocationTable: CurrentLocationTable): Long?

    @Query("DELETE FROM CurrentLocationTable WHERE currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey")
    fun deleteCurrentLocation(currentLocationTablePrimaryKey: Long)

    @Query("DELETE FROM CurrentLocationTable WHERE currentLocationTableFirebaseKey = :currentLocationTableFirebaseKey")
    fun deleteCurrentLocationByCurrentLocationFirebaseKey(currentLocationTableFirebaseKey: String?)

    @Query("DELETE FROM CurrentLocationTable")
    fun deleteAllCurrentLocations()

    @get:Query("SELECT * FROM CurrentLocationTable")
    val allCurrentLocation: LiveData<MutableList<CurrentLocationTable?>?>

    @Query("SELECT * FROM CurrentLocationTable WHERE currentLocationContactEmailAddress = :currentLocationContactEmailAddress ORDER BY currentLocationSequenceKey ASC")
    fun findAllCurrentLocation(currentLocationContactEmailAddress: String?): MutableList<CurrentLocationTable?>?

    @Query("SELECT * FROM CurrentLocationTable  WHERE currentLocationTableFirebaseKey = :currentLocationTableFirebaseKey")
    fun findCurrentLocationTableByFirebaseKey(currentLocationTableFirebaseKey: String?): CurrentLocationTable?

    @Query("SELECT * FROM CurrentLocationTable WHERE currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey")
    fun findCurrentLocationByCurrentLocationTablePrimaryKey(currentLocationTablePrimaryKey: Long): CurrentLocationTable?

    @Query(
        "UPDATE CurrentLocationTable SET " +
                "currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey, " +
                "currentLocationTableFirebaseKey = :currentLocationTableFirebaseKey, " +
                "currentLocationContactEmailAddress = :currentLocationContactEmailAddress, " +
                "currentLocationTransactionType = :currentLocationTransactionType, " +
                "currentLocationSequenceKey = :currentLocationSequenceKey, " +
                "currentLocationAndDateTimeString = :currentLocationAndDateTimeString " +
                "WHERE currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey"
    )
    fun updateCurrentLocation(
        currentLocationTablePrimaryKey: Long,
        currentLocationTableFirebaseKey: String?,
        currentLocationContactEmailAddress: String?,
        currentLocationTransactionType: String?,
        currentLocationSequenceKey: Long,
        currentLocationAndDateTimeString: String?
    )
}

MainViewModel中的DAO初始化与定义

// init代码块
val db: PeepsConnectionRoomDatabase
db = PeepsConnectionRoomDatabase.getDatabase(appContext)!!
currentLocationTableDao = db.currentLocationTableDao()!!
customGeofencePerimeterMarkerTableDao = db.customGeofencePerimeterMarkerTableDao()!!;

// DAO变量定义
var checkInRouteAddressTableDao: CheckInRouteAddressTableDao
var customGeofencePerimeterMarkerTableDao: CustomGeofencePerimeterMarkerTableDao

出现错误的插入函数代码

@Throws(ExecutionException::class, InterruptedException::class)
fun insertCurrentLocation(currentLocationTable: CurrentLocationTable?): Long? {
    Log.i(" ", "MainViewModel insertCurrentLocation: ")
    mCurrentLocationTable = currentLocationTable
    val insertCurrentLocationTask = InsertCurrentLocationTask(currentLocationTable)
    val future = executor.submit(insertCurrentLocationTask) ?: null
    return future?.get()
}

internal class InsertCurrentLocationTask(mCurrentLocationTable: CurrentLocationTable?) :
    Callable<Long?> {
    @Throws(Exception::class)
    override fun call(): Long? {
        return currentLocationTableDao.insertCurrentLocation(mCurrentLocationTable) // currentLocationTableDao未解析
    }
}

@Throws(ExecutionException::class, InterruptedException::class)
fun insertCustomGeofencePerimeterMarker(customGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable?): Long? {
    Log.i(" ", "MainViewModel insertCustomGeofencePerimeterMarker: ")
    val insertCustomGeofencePerimeterMarkerTask =
        InsertCustomGeofencePerimeterMarkerTask(customGeofencePerimeterMarkerTable)
    val future = executor.submit(insertCustomGeofencePerimeterMarkerTask) ?: null
    return future?.get()
}

internal class InsertCustomGeofencePerimeterMarkerTask(mCustomGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable?) :
    Callable<Long?> {
    @Throws(Exception::class)
    override fun call(): Long {
        return customGeofencePerimeterMarkerTableDao.insertCustomGeofencePerimeterMarkerTable(mCustomGeofencePerimeterMarkerTable) // customGeofencePerimeterMarkerTableDao未解析
    }
}

解决方案

问题核心是Kotlin中嵌套类默认无法访问外部类的非final成员,在KMP环境下该访问规则更严格,可通过以下方式解决:

方式1:将DAO变量改为val

把MainViewModel中的DAO变量定义从var改为val,Kotlin嵌套类可直接访问外部类的不可变成员:

// 修改DAO变量定义
val checkInRouteAddressTableDao: CheckInRouteAddressTableDao
val customGeofencePerimeterMarkerTableDao: CustomGeofencePerimeterMarkerTableDao

方式2:用inner修饰内部任务类

若需保留var,给内部任务类添加inner关键字,使其成为内部类(而非嵌套类),即可访问外部类成员:

internal inner class InsertCurrentLocationTask(mCurrentLocationTable: CurrentLocationTable?) :
    Callable<Long?> {
    @Throws(Exception::class)
    override fun call(): Long? {
        return currentLocationTableDao.insertCurrentLocation(mCurrentLocationTable)
    }
}

internal inner class InsertCustomGeofencePerimeterMarkerTask(mCustomGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable?) :
    Callable<Long?> {
    @Throws(Exception::class)
    override fun call(): Long {
        return customGeofencePerimeterMarkerTableDao.insertCustomGeofencePerimeterMarkerTable(mCustomGeofencePerimeterMarkerTable)
    }
}

方式3:将DAO作为参数传入内部任务类

直接把DAO实例传递给内部任务类,避免依赖外部类成员访问:

// 修改任务类
internal class InsertCurrentLocationTask(
    private val dao: CurrentLocationTableDao,
    private val mCurrentLocationTable: CurrentLocationTable?
) : Callable<Long?> {
    @Throws(Exception::class)
    override fun call(): Long? {
        return dao.insertCurrentLocation(mCurrentLocationTable)
    }
}

// 创建任务时传入DAO
val insertCurrentLocationTask = InsertCurrentLocationTask(currentLocationTableDao, currentLocationTable)

对应修改另一个任务类:

internal class InsertCustomGeofencePerimeterMarkerTask(
    private val dao: CustomGeofencePerimeterMarkerTableDao,
    private val mCustomGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable?
) : Callable<Long?> {
    @Throws(Exception::class)
    override fun call(): Long {
        return dao.insertCustomGeofencePerimeterMarkerTable(mCustomGeofencePerimeterMarkerTable)
    }
}

// 创建任务时传入DAO
val insertCustomGeofencePerimeterMarkerTask = InsertCustomGeofencePerimeterMarkerTask(customGeofencePerimeterMarkerTableDao, customGeofencePerimeterMarkerTable)

额外建议

在Kotlin Multiplatform中,建议用协程替代手动创建Callable任务处理Room异步操作,Room 2.1.0+支持协程挂起函数,代码更简洁且符合KMP最佳实践:

// 修改DAO中的插入方法为挂起函数
@Insert
suspend fun insertCurrentLocation(currentLocationTable: CurrentLocationTable): Long?

// MainViewModel中用协程调用
suspend fun insertCurrentLocation(currentLocationTable: CurrentLocationTable?): Long? {
    currentLocationTable ?: return null
    return withContext(Dispatchers.IO) {
        currentLocationTableDao.insertCurrentLocation(currentLocationTable)
    }
}

内容的提问来源于stack exchange,提问作者GeorgeRussell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:04:55