Mongodb Mongo和Kotlin中多个集合的通用getter
我使用Kotlin和Mongo(with),我有多个模型,如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
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()
中genericT
的类型,这就是类型推断失败的原因。解决这个问题的一个方法是在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:
注:
CoroutineCollection
替换为MongoCollection
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,
)