【问题标题】:Generic getter for multiple Collection in Mongo with Kotlin使用 Kotlin 在 Mongo 中用于多个 Collection 的通用 getter
【发布时间】:2018-05-03 12:28:12
【问题描述】:

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

所以我将通用基础中的特定实体传递为:

class UserDao : BaseDao<UserEntity>() { ... }

这个基类实现的泛型方法如下:

open class BaseDao<T: Any>() {

    fun get(id: String): T? {
        return getCollection().findOneById(id)
    }

    fun save(entity: T): T {
        return getCollection().save(entity)
    }

    fun delete(id: String) {
        getCollection().deleteOneById(id)
    }
    ...
}

但是,getCollection() 方法出现问题:

private inline fun <reified T: Any> getCollection(): MongoCollection<T> {
    return MongoDb.getDatabase().getCollection<T>()
}

每次调用时都会出现编译错误:

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.

我找不到执行此操作的正确方法。我已经检查了这些线程,但没有成功:Generic class type usage in Kotlin & Kotlin abstract class with generic param and methods which use type param

问题:

我怎样才能实现这个通用的BaseDao,它应该得到每个孩子Dao的任何集合?

【问题讨论】:

  • 看来你根本不需要&lt;reified T : Any&gt;。试试&lt;T&gt;

标签: mongodb generics kotlin


【解决方案1】:

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

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

在这之后,给你的具体化函数一个接受`KClass的参数:

private inline fun <reified T: Any> getCollection(val kClass: KClass<T>):  MongoCollection<T> {
    return MongoDb.getDatabase().getCollection<T>()
}

我不知道在不将KClass 作为参数传递给函数的情况下执行此操作的方法,但这应该有效,因为通用T 可以从提供的kClass 派生。 `

另一种方法是使 BaseDao 中的所有方法都具有具体泛型的内联函数,并将泛型放在类上。

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)
    }
    ...
}

这样可以导出泛型T,因为调用getCollection的方法也被具体化了。

【讨论】:

  • 感谢您的回复,但是您在getCollection方法中传递的参数kClass根本没有使用。正常吗?我会避免第二种方式,因为我需要覆盖 BaseDao 中的其他方法,但使用 inline 仅适用于 privatefinal 成员。
【解决方案2】:

(对于 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)
    }
}

并在特定的 DAO 中实现,例如 SessionDao

class SessionDao(collection: CoroutineCollection<DbSession>) 
      : BaseDao<DbSession>(collection)

(注意:如果这样感觉更好,可以使用by 关键字将继承替换为委托

这个和其他 dao 可以通过 DI 或某种 dao factory 创建:

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())
}


注意事项:

  1. 这个例子是基于协程的kmongo,通过将CoroutineCollection替换成MongoCollection可以很容易的转换成阻塞kmongo
  2. 我假设文档 ID 是通过 ID 容器注释的,这有助于减少错误,因此应该以这种方式创建文档:
     data class DbSession(
         @BsonId
         val id: Id<DbSession>,
    
         val name: String,
     ) 
    
    

【讨论】:

    【解决方案3】:

    解决方案是使用反射作为Zigzago mentioned,使用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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-29
      • 2017-04-26
      • 1970-01-01
      • 2018-08-18
      • 2015-10-30
      • 2020-03-06
      • 2017-12-16
      • 1970-01-01
      相关资源
      最近更新 更多