Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mongodb Mongo和Kotlin中多个集合的通用getter_Mongodb_Generics_Kotlin - Fatal编程技术网

Mongodb Mongo和Kotlin中多个集合的通用getter

Mongodb Mongo和Kotlin中多个集合的通用getter,mongodb,generics,kotlin,Mongodb,Generics,Kotlin,我使用Kotlin和Mongo(with),我有多个模型,如UserEntity,MovieEntity等等。它们中的每一个都使用一个特定的Dao类来执行(实际上)相同的方法。因此,我试图通过使用BaseDao来避免任何重复,它应该使用这些方法 因此,我将泛型库中的特定实体传递为: class UserDao : BaseDao<UserEntity>() { ... } 每次我调用它时,它都会得到一个编译错误: Type inference failed: Not enough

我使用Kotlin和Mongo(with),我有多个模型,如
UserEntity
MovieEntity
等等。它们中的每一个都使用一个特定的
Dao
类来执行(实际上)相同的方法。因此,我试图通过使用
BaseDao
来避免任何重复,它应该使用这些方法

因此,我将泛型库中的特定实体传递为:

class UserDao : BaseDao<UserEntity>() { ... }
每次我调用它时,它都会得到一个编译错误:

Type inference failed: Not enough information to infer parameter T in 
inline fun <reified T : Any> getCollection(): MongoCollection<T#1 (type parameter of app.api.db.dao.BaseDao.getCollection)>  
Please specify it explicitly.
类型推断失败:信息不足,无法推断中的参数T
内联fun getCollection():MongoCollection
请明确指定它。
我找不到正确的方法来做这件事。我已经检查了这些线程,但没有成功:&

问题:


如何实现这个generic
BaseDao
,它应该获得每个子
Dao
的任何集合?

JVM在运行时忘记
BaseDao()
中generic
T
的类型,这就是类型推断失败的原因。解决这个问题的一个方法是在BaseDao的构造函数中传递T的KClass:

open class BaseDao<T: Any>(val kClass: KClass<T>) {
    ...
}

通过这种方式可以导出泛型
T
,因为调用
getCollection
的方法也被具体化。

解决方案是使用反射,就像使用
KMongoUtil
一样:

protected fun getCollection(): MongoCollection<T> =
    getDaoEntityClass().let { k ->
        MongoDb.getDatabase().getCollection(
            KMongoUtil.defaultCollectionName(k), k.java)
    }

@Suppress("UNCHECKED_CAST")
private fun getDaoEntityClass(): KClass<T>
    = ((this::class.java.genericSuperclass
        as ParameterizedType).actualTypeArguments[0] as Class<T>).kotlin
getCollection():MongoCollection=
getDaoEntityClass().let{k->
MongoDb.getDatabase().getCollection(
KMongoUtil.defaultCollectionName(k),k.java)
}
@抑制(“未选中的_CAST”)
private fun getDaoEntityClass():KClass
=((this::class.java.genericSuperclass)
作为参数化类型)。实际类型参数[0]作为类)。kotlin
(对于KMongo 4.0++)无需为每个方法使用具体化的泛型,而是可以将此基类用作起点:

open class BaseDao<T: Any>(
    protected val collection: CoroutineCollection<T>
) {

    suspend fun get(id: Id<T>): T? {
        return collection.findOneById(id)
    }

    suspend fun save(entity: T): UpdateResult? {
        return collection.save(entity)
    }

    suspend fun delete(id: Id<T>) {
        collection.deleteOneById(id)
    }
}
(注意:如果您感觉更好,可以使用
by
关键字将继承替换为委派。)

可以通过DI或某种dao工厂创建此dao和其他dao:


注:

  • 此示例适用于基于协同路由的kmongo,通过将
    CoroutineCollection
    替换为
    MongoCollection
  • 我假设文档id是通过id容器进行注释的,这有助于减少错误,因此应该以这种方式创建文档:
     data class DbSession(
         @BsonId
         val id: Id<DbSession>,
    
         val name: String,
     ) 
    
    
    数据类DbSession(
    @BsonId
    val id:id,
    val name:String,
    ) 
    

  • 谢谢你的回复,但是你传入的
    getCollection
    方法中的参数
    kClass
    根本没有被使用。这正常吗?我会避免第二种方法,因为我需要覆盖
    BaseDao
    中的其他方法,但是使用
    inline
    仅在
    private
    final
    成员上。看起来你没有根本不需要。只需试试
    open class BaseDao() {
    
        inline fun <reified T: Any> get(id: String): T? {
            return getCollection().findOneById(id)
        }
    
        inline fun <reified T: Any> save (entity: T): T {
            return getCollection().save(entity)
        }
    
        inline fun <reified T: Any> delete(id: String) {
            getCollection().deleteOneById(id)
        }
        ...
    }
    
    protected fun getCollection(): MongoCollection<T> =
        getDaoEntityClass().let { k ->
            MongoDb.getDatabase().getCollection(
                KMongoUtil.defaultCollectionName(k), k.java)
        }
    
    @Suppress("UNCHECKED_CAST")
    private fun getDaoEntityClass(): KClass<T>
        = ((this::class.java.genericSuperclass
            as ParameterizedType).actualTypeArguments[0] as Class<T>).kotlin
    
    open class BaseDao<T: Any>(
        protected val collection: CoroutineCollection<T>
    ) {
    
        suspend fun get(id: Id<T>): T? {
            return collection.findOneById(id)
        }
    
        suspend fun save(entity: T): UpdateResult? {
            return collection.save(entity)
        }
    
        suspend fun delete(id: Id<T>) {
            collection.deleteOneById(id)
        }
    }
    
    class SessionDao(collection: CoroutineCollection<DbSession>) 
          : BaseDao<DbSession>(collection)
    
    class DbInstance(mongodbConnectionString: String = "mongodb://localhost:27017/myproject") {
        private val connectionInfo = ConnectionString(mongodbConnectionString)
        val client = KMongo.createClient().coroutine
        val db = client.getDatabase(
            connectionInfo.database ?: throw IllegalArgumentException("mongodb connection string must include db name")
        )
    
    
        val sessions = SessionDao(db.getCollection())
    }
    
    
     data class DbSession(
         @BsonId
         val id: Id<DbSession>,
    
         val name: String,
     )